From c2e4e981b3b5d4cc984b6288384bfd36880f5d57 Mon Sep 17 00:00:00 2001 From: +merlan #flirora Date: Fri, 16 Jun 2023 03:04:34 -0400 Subject: [PATCH 001/113] Add more comprehensive tests for is_sorted and friends See #53485 and #55045. --- library/core/tests/iter/traits/iterator.rs | 31 +++++++++++++++++++++- library/core/tests/slice.rs | 28 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/library/core/tests/iter/traits/iterator.rs b/library/core/tests/iter/traits/iterator.rs index 9eebfb1f1f35..995bbf0e2615 100644 --- a/library/core/tests/iter/traits/iterator.rs +++ b/library/core/tests/iter/traits/iterator.rs @@ -1,3 +1,4 @@ +use core::cmp::Ordering; use core::num::NonZeroUsize; /// A wrapper struct that implements `Eq` and `Ord` based on the wrapped @@ -371,11 +372,39 @@ fn test_by_ref() { #[test] fn test_is_sorted() { + // Tests on integers assert!([1, 2, 2, 9].iter().is_sorted()); assert!(![1, 3, 2].iter().is_sorted()); assert!([0].iter().is_sorted()); - assert!(std::iter::empty::().is_sorted()); + assert!([0, 0].iter().is_sorted()); + assert!(core::iter::empty::().is_sorted()); + + // Tests on floats + assert!([1.0f32, 2.0, 2.0, 9.0].iter().is_sorted()); + assert!(![1.0f32, 3.0f32, 2.0f32].iter().is_sorted()); + assert!([0.0f32].iter().is_sorted()); + assert!([0.0f32, 0.0f32].iter().is_sorted()); + // Test cases with NaNs + assert!([f32::NAN].iter().is_sorted()); + assert!(![f32::NAN, f32::NAN].iter().is_sorted()); assert!(![0.0, 1.0, f32::NAN].iter().is_sorted()); + // Tests from + assert!(![f32::NAN, f32::NAN, f32::NAN].iter().is_sorted()); + assert!(![1.0, f32::NAN, 2.0].iter().is_sorted()); + assert!(![2.0, f32::NAN, 1.0].iter().is_sorted()); + assert!(![2.0, f32::NAN, 1.0, 7.0].iter().is_sorted()); + assert!(![2.0, f32::NAN, 1.0, 0.0].iter().is_sorted()); + assert!(![-f32::NAN, -1.0, 0.0, 1.0, f32::NAN].iter().is_sorted()); + assert!(![f32::NAN, -f32::NAN, -1.0, 0.0, 1.0].iter().is_sorted()); + assert!(![1.0, f32::NAN, -f32::NAN, -1.0, 0.0].iter().is_sorted()); + assert!(![0.0, 1.0, f32::NAN, -f32::NAN, -1.0].iter().is_sorted()); + assert!(![-1.0, 0.0, 1.0, f32::NAN, -f32::NAN].iter().is_sorted()); + + // Tests for is_sorted_by + assert!(![6, 2, 8, 5, 1, -60, 1337].iter().is_sorted()); + assert!([6, 2, 8, 5, 1, -60, 1337].iter().is_sorted_by(|_, _| Some(Ordering::Less))); + + // Tests for is_sorted_by_key assert!([-2, -1, 0, 3].iter().is_sorted()); assert!(![-2i32, -1, 0, 3].iter().is_sorted_by_key(|n| n.abs())); assert!(!["c", "bb", "aaa"].iter().is_sorted()); diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index 88f54591bb4a..865e702b5c21 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -2278,11 +2278,39 @@ fn test_copy_within_panics_src_out_of_bounds() { fn test_is_sorted() { let empty: [i32; 0] = []; + // Tests on integers assert!([1, 2, 2, 9].is_sorted()); assert!(![1, 3, 2].is_sorted()); assert!([0].is_sorted()); + assert!([0, 0].is_sorted()); assert!(empty.is_sorted()); + + // Tests on floats + assert!([1.0f32, 2.0, 2.0, 9.0].is_sorted()); + assert!(![1.0f32, 3.0f32, 2.0f32].is_sorted()); + assert!([0.0f32].is_sorted()); + assert!([0.0f32, 0.0f32].is_sorted()); + // Test cases with NaNs + assert!([f32::NAN].is_sorted()); + assert!(![f32::NAN, f32::NAN].is_sorted()); assert!(![0.0, 1.0, f32::NAN].is_sorted()); + // Tests from + assert!(![f32::NAN, f32::NAN, f32::NAN].is_sorted()); + assert!(![1.0, f32::NAN, 2.0].is_sorted()); + assert!(![2.0, f32::NAN, 1.0].is_sorted()); + assert!(![2.0, f32::NAN, 1.0, 7.0].is_sorted()); + assert!(![2.0, f32::NAN, 1.0, 0.0].is_sorted()); + assert!(![-f32::NAN, -1.0, 0.0, 1.0, f32::NAN].is_sorted()); + assert!(![f32::NAN, -f32::NAN, -1.0, 0.0, 1.0].is_sorted()); + assert!(![1.0, f32::NAN, -f32::NAN, -1.0, 0.0].is_sorted()); + assert!(![0.0, 1.0, f32::NAN, -f32::NAN, -1.0].is_sorted()); + assert!(![-1.0, 0.0, 1.0, f32::NAN, -f32::NAN].is_sorted()); + + // Tests for is_sorted_by + assert!(![6, 2, 8, 5, 1, -60, 1337].is_sorted()); + assert!([6, 2, 8, 5, 1, -60, 1337].is_sorted_by(|_, _| Some(Ordering::Less))); + + // Tests for is_sorted_by_key assert!([-2, -1, 0, 3].is_sorted()); assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs())); assert!(!["c", "bb", "aaa"].is_sorted()); From c67aea7fddcf613abd3c76ee4ac31eced6da8265 Mon Sep 17 00:00:00 2001 From: wackbyte Date: Mon, 24 Apr 2023 10:14:03 -0400 Subject: [PATCH 002/113] rustdoc: fix position of `default` in method rendering --- src/librustdoc/html/render/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index f923f9054512..4ba648f8ad14 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -820,6 +820,7 @@ fn assoc_method( let header = meth.fn_header(tcx).expect("Trying to get header from a non-function item"); let name = meth.name.as_ref().unwrap(); let vis = visibility_print_with_space(meth.visibility(tcx), meth.item_id, cx).to_string(); + let defaultness = print_default_space(meth.is_default()); // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove // this condition. let constness = match render_mode { @@ -830,7 +831,6 @@ fn assoc_method( }; let asyncness = header.asyncness.print_with_space(); let unsafety = header.unsafety.print_with_space(); - let defaultness = print_default_space(meth.is_default()); let abi = print_abi_with_space(header.abi).to_string(); let href = assoc_href_attr(meth, link, cx); @@ -838,10 +838,10 @@ fn assoc_method( let generics_len = format!("{:#}", g.print(cx)).len(); let mut header_len = "fn ".len() + vis.len() + + defaultness.len() + constness.len() + asyncness.len() + unsafety.len() - + defaultness.len() + abi.len() + name.as_str().len() + generics_len; @@ -860,14 +860,14 @@ fn assoc_method( w.reserve(header_len + "{".len() + "".len()); write!( w, - "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn \ + "{indent}{vis}{defaultness}{constness}{asyncness}{unsafety}{abi}fn \ {name}{generics}{decl}{notable_traits}{where_clause}", indent = indent_str, vis = vis, + defaultness = defaultness, constness = constness, asyncness = asyncness, unsafety = unsafety, - defaultness = defaultness, abi = abi, href = href, name = name, From 13f58a8ea02fa6d62b0c44ceda9d5332562e1339 Mon Sep 17 00:00:00 2001 From: wackbyte Date: Thu, 22 Jun 2023 22:28:29 -0400 Subject: [PATCH 003/113] Add tests for default unsafe trait methods --- tests/rustdoc/default-trait-method.rs | 43 +++++++++++++++++++-------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/tests/rustdoc/default-trait-method.rs b/tests/rustdoc/default-trait-method.rs index 6d0e339c48dc..c89506781640 100644 --- a/tests/rustdoc/default-trait-method.rs +++ b/tests/rustdoc/default-trait-method.rs @@ -1,26 +1,45 @@ #![feature(min_specialization)] // @has default_trait_method/trait.Item.html -// @has - '//*[@id="tymethod.foo"]' 'fn foo()' -// @!has - '//*[@id="tymethod.foo"]' 'default fn foo()' -// @has - '//*[@id="tymethod.bar"]' 'fn bar()' -// @!has - '//*[@id="tymethod.bar"]' 'default fn bar()' -// @has - '//*[@id="method.baz"]' 'fn baz()' -// @!has - '//*[@id="method.baz"]' 'default fn baz()' pub trait Item { + // @has - '//*[@id="tymethod.foo"]' 'fn foo()' + // @!has - '//*[@id="tymethod.foo"]' 'default fn foo()' fn foo(); + + // @has - '//*[@id="tymethod.bar"]' 'fn bar()' + // @!has - '//*[@id="tymethod.bar"]' 'default fn bar()' fn bar(); - fn baz() {} + + // @has - '//*[@id="tymethod.baz"]' 'unsafe fn baz()' + // @!has - '//*[@id="tymethod.baz"]' 'default unsafe fn baz()' + unsafe fn baz(); + + // @has - '//*[@id="tymethod.quux"]' 'unsafe fn quux()' + // @!has - '//*[@id="tymethod.quux"]' 'default unsafe fn quux()' + unsafe fn quux(); + + // @has - '//*[@id="method.xyzzy"]' 'fn xyzzy()' + // @!has - '//*[@id="method.xyzzy"]' 'default fn xyzzy()' + fn xyzzy() {} } // @has default_trait_method/struct.Foo.html -// @has - '//*[@id="method.foo"]' 'default fn foo()' -// @has - '//*[@id="method.bar"]' 'fn bar()' -// @!has - '//*[@id="method.bar"]' 'default fn bar()' -// @has - '//*[@id="method.baz"]' 'fn baz()' -// @!has - '//*[@id="method.baz"]' 'default fn baz()' pub struct Foo; impl Item for Foo { + // @has - '//*[@id="method.foo"]' 'default fn foo()' default fn foo() {} + + // @has - '//*[@id="method.bar"]' 'fn bar()' + // @!has - '//*[@id="method.bar"]' 'default fn bar()' fn bar() {} + + // @has - '//*[@id="method.baz"]' 'default unsafe fn baz()' + default unsafe fn baz() {} + + // @has - '//*[@id="method.quux"]' 'unsafe fn quux()' + // @!has - '//*[@id="method.quux"]' 'default unsafe fn quux()' + unsafe fn quux() {} + + // @has - '//*[@id="method.xyzzy"]' 'fn xyzzy()' + // @!has - '//*[@id="method.xyzzy"]' 'default fn xyzzy()' } From 11f35d60163c659e2960f2749f7db732d3fc9000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20M=2E=20Bezerra?= Date: Tue, 27 Jun 2023 01:05:48 -0300 Subject: [PATCH 004/113] std: remove an allocation in `Path::with_extension` `Path::with_extension` used to reallocate (and copy) paths twice per call, now it does it once, by checking the size of the previous and new extensions it's possible to call `PathBuf::with_capacity` and pass the exact capacity it takes. Also reduce the memory consumption of the path returned from `Path::with_extension` by using exact capacity instead of using amortized exponential growth. --- library/std/src/path.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 28cd3c4e4dbd..99f7a60f8ab0 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2608,9 +2608,27 @@ impl Path { } fn _with_extension(&self, extension: &OsStr) -> PathBuf { - let mut buf = self.to_path_buf(); - buf.set_extension(extension); - buf + let self_len = self.as_os_str().len(); + let self_bytes = self.as_os_str().as_os_str_bytes(); + + let (new_capacity, slice_to_copy) = match self.extension() { + None => { + // Enough capacity for the extension and the dot + let capacity = self_len + extension.len() + 1; + let whole_path = self_bytes.iter(); + (capacity, whole_path) + } + Some(previous_extension) => { + let capacity = self_len + extension.len() - previous_extension.len(); + let path_till_dot = self_bytes[..self_len - previous_extension.len()].iter(); + (capacity, path_till_dot) + } + }; + + let mut new_path = PathBuf::with_capacity(new_capacity); + new_path.as_mut_vec().extend(slice_to_copy); + new_path.set_extension(extension); + new_path } /// Produces an iterator over the [`Component`]s of the path. From 6ffca76e9b9bbc7757d2167cdf07c193e357d69c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20M=2E=20Bezerra?= Date: Fri, 14 Jul 2023 13:19:45 -0300 Subject: [PATCH 005/113] std: add tests for `Path::with_extension` --- library/std/src/path/tests.rs | 50 +++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index dd307022c6d0..f12ffbf2e016 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -1183,7 +1183,7 @@ pub fn test_prefix_ext() { #[test] pub fn test_push() { macro_rules! tp ( - ($path:expr, $push:expr, $expected:expr) => ( { + ($path:expr, $push:expr, $expected:expr) => ({ let mut actual = PathBuf::from($path); actual.push($push); assert!(actual.to_str() == Some($expected), @@ -1281,7 +1281,7 @@ pub fn test_push() { #[test] pub fn test_pop() { macro_rules! tp ( - ($path:expr, $expected:expr, $output:expr) => ( { + ($path:expr, $expected:expr, $output:expr) => ({ let mut actual = PathBuf::from($path); let output = actual.pop(); assert!(actual.to_str() == Some($expected) && output == $output, @@ -1335,7 +1335,7 @@ pub fn test_pop() { #[test] pub fn test_set_file_name() { macro_rules! tfn ( - ($path:expr, $file:expr, $expected:expr) => ( { + ($path:expr, $file:expr, $expected:expr) => ({ let mut p = PathBuf::from($path); p.set_file_name($file); assert!(p.to_str() == Some($expected), @@ -1369,7 +1369,7 @@ pub fn test_set_file_name() { #[test] pub fn test_set_extension() { macro_rules! tfe ( - ($path:expr, $ext:expr, $expected:expr, $output:expr) => ( { + ($path:expr, $ext:expr, $expected:expr, $output:expr) => ({ let mut p = PathBuf::from($path); let output = p.set_extension($ext); assert!(p.to_str() == Some($expected) && output == $output, @@ -1394,6 +1394,46 @@ pub fn test_set_extension() { tfe!("/", "foo", "/", false); } +#[test] +pub fn test_with_extension() { + macro_rules! twe ( + ($input:expr, $extension:expr, $expected:expr) => ({ + let input = Path::new($input); + let output = input.with_extension($extension); + + assert!( + output.to_str() == Some($expected), + "calling Path::new({:?}).with_extension({:?}): Expected {:?}, got {:?}", + $input, $extension, $expected, output, + ); + }); + ); + + twe!("foo", "txt", "foo.txt"); + twe!("foo.bar", "txt", "foo.txt"); + twe!("foo.bar.baz", "txt", "foo.bar.txt"); + twe!(".test", "txt", ".test.txt"); + twe!("foo.txt", "", "foo"); + twe!("foo", "", "foo"); + twe!("", "foo", ""); + twe!(".", "foo", "."); + twe!("foo/", "bar", "foo.bar"); + twe!("foo/.", "bar", "foo.bar"); + twe!("..", "foo", ".."); + twe!("foo/..", "bar", "foo/.."); + twe!("/", "foo", "/"); + + // New extension is smaller than file name + twe!("aaa_aaa_aaa", "bbb_bbb", "aaa_aaa_aaa.bbb_bbb"); + // New extension is greater than file name + twe!("bbb_bbb", "aaa_aaa_aaa", "bbb_bbb.aaa_aaa_aaa"); + + // New extension is smaller than previous extension + twe!("ccc.aaa_aaa_aaa", "bbb_bbb", "ccc.bbb_bbb"); + // New extension is greater than previous extension + twe!("ccc.bbb_bbb", "aaa_aaa_aaa", "ccc.aaa_aaa_aaa"); +} + #[test] fn test_eq_receivers() { use crate::borrow::Cow; @@ -1669,7 +1709,7 @@ fn into_rc() { #[test] fn test_ord() { macro_rules! ord( - ($ord:ident, $left:expr, $right:expr) => ( { + ($ord:ident, $left:expr, $right:expr) => ({ use core::cmp::Ordering; let left = Path::new($left); From fcdff634cfd14ef624f1643a9ca8afc104068fcf Mon Sep 17 00:00:00 2001 From: Arlie Davis Date: Fri, 14 Jul 2023 14:30:06 -0700 Subject: [PATCH 006/113] Use SHA256 by default when targeting MSVC --- compiler/rustc_session/src/session.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 5be122ffbdeb..6f4bad9bc151 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1420,7 +1420,7 @@ pub fn build_session( let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); let hash_kind = sopts.unstable_opts.src_hash_algorithm.unwrap_or_else(|| { if target_cfg.is_like_msvc { - SourceFileHashAlgorithm::Sha1 + SourceFileHashAlgorithm::Sha256 } else { SourceFileHashAlgorithm::Md5 } From 2d47816cbaebb3b8f400b11fa122feae00fd5c58 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sat, 5 Nov 2022 01:08:57 -0700 Subject: [PATCH 007/113] rustc_llvm: Add a `-Z print-llvm-stats` option to expose LLVM statistics. LLVM has a neat [statistics] feature that tracks how often optimizations kick in. It's very handy for optimization work. Since we expose the LLVM pass timings, I thought it made sense to expose the LLVM statistics too. [statistics]: https://llvm.org/docs/ProgrammersManual.html#the-statistic-class-stats-option --- compiler/rustc_codegen_gcc/src/lib.rs | 4 ++++ compiler/rustc_codegen_llvm/src/lib.rs | 5 +++++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 3 +++ compiler/rustc_codegen_llvm/src/llvm_util.rs | 4 ++++ compiler/rustc_codegen_ssa/src/back/write.rs | 4 ++++ compiler/rustc_codegen_ssa/src/traits/write.rs | 1 + compiler/rustc_interface/src/tests.rs | 1 + compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 6 ++++++ compiler/rustc_session/src/options.rs | 3 +++ compiler/rustc_session/src/session.rs | 4 ++++ 10 files changed, 35 insertions(+) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 2a6b642782df..04ac0254a81b 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -239,6 +239,10 @@ impl WriteBackendMethods for GccCodegenBackend { unimplemented!(); } + fn print_statistics(&self) { + unimplemented!() + } + unsafe fn optimize(_cgcx: &CodegenContext, _diag_handler: &Handler, module: &ModuleCodegen, config: &ModuleConfig) -> Result<(), FatalError> { module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level)); Ok(()) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 24ba28bbc82c..713c22ebfeba 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -181,6 +181,11 @@ impl WriteBackendMethods for LlvmCodegenBackend { llvm::LLVMRustPrintPassTimings(); } } + fn print_statistics(&self) { + unsafe { + llvm::LLVMRustPrintStatistics(); + } + } fn run_link( cgcx: &CodegenContext, diag_handler: &Handler, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 605f0154773a..3eb045557497 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1870,6 +1870,9 @@ extern "C" { /// Print the pass timings since static dtors aren't picking them up. pub fn LLVMRustPrintPassTimings(); + /// Print the statistics since static dtors aren't picking them up. + pub fn LLVMRustPrintStatistics(); + pub fn LLVMStructCreateNamed(C: &Context, Name: *const c_char) -> &Type; pub fn LLVMStructSetBody<'a>( diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 03be0654b50b..12649de5866f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -110,6 +110,10 @@ unsafe fn configure_llvm(sess: &Session) { // Use non-zero `import-instr-limit` multiplier for cold callsites. add("-import-cold-multiplier=0.1", false); + if sess.print_llvm_stats() { + add("-stats", false); + } + for arg in sess_args { add(&(*arg), true); } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index ececa29b2315..0ce6129a4625 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1945,6 +1945,10 @@ impl OngoingCodegen { self.backend.print_pass_timings() } + if sess.print_llvm_stats() { + self.backend.print_statistics() + } + ( CodegenResults { metadata: self.metadata, diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 9826256a4c5d..a3b11ed4fe8f 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -35,6 +35,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { cached_modules: Vec<(SerializedModule, WorkProduct)>, ) -> Result<(Vec>, Vec), FatalError>; fn print_pass_timings(&self); + fn print_statistics(&self); unsafe fn optimize( cgcx: &CodegenContext, diag_handler: &Handler, diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 09141afd1370..0eac098e8a3e 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -715,6 +715,7 @@ fn test_unstable_options_tracking_hash() { // `pre_link_arg` is omitted because it just forwards to `pre_link_args`. untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]); untracked!(print_llvm_passes, true); + untracked!(print_llvm_stats, true); untracked!(print_mono_items, Some(String::from("abc"))); untracked!(print_type_sizes, true); untracked!(proc_macro_backtrace, true); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index ab5fa961b95a..89beb09db755 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1,4 +1,5 @@ #include "LLVMWrapper.h" +#include "llvm/ADT/Statistic.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DiagnosticHandler.h" #include "llvm/IR/DiagnosticInfo.h" @@ -116,6 +117,11 @@ extern "C" void LLVMRustPrintPassTimings() { TimerGroup::printAll(OS); } +extern "C" void LLVMRustPrintStatistics() { + raw_fd_ostream OS(2, false); // stderr. + llvm::PrintStatistics(OS); +} + extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name, size_t NameLen) { return wrap(unwrap(M)->getNamedValue(StringRef(Name, NameLen))); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 87d67c099ced..90007d7c8496 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1672,6 +1672,9 @@ options! { "make rustc print the total optimization fuel used by a crate"), print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "print the LLVM optimization passes being run (default: no)"), + #[rustc_lint_opt_deny_field_access("use `Session::print_llvm_stats` instead of this field")] + print_llvm_stats: bool = (true, parse_bool, [UNTRACKED], + "print LLVM statistics (default: no)"), print_mono_items: Option = (None, parse_opt_string, [UNTRACKED], "print the result of the monomorphization collection pass"), print_type_sizes: bool = (false, parse_bool, [UNTRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 5be122ffbdeb..c2588b9a99a1 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1057,6 +1057,10 @@ impl Session { self.opts.unstable_opts.verbose } + pub fn print_llvm_stats(&self) -> bool { + self.opts.unstable_opts.print_llvm_stats + } + pub fn verify_llvm_ir(&self) -> bool { self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some() } From 138f522b590492d1ef80f1483382a2a678dec7d9 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sat, 5 Nov 2022 01:13:42 -0700 Subject: [PATCH 008/113] Don't enable by default :) --- compiler/rustc_session/src/options.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 90007d7c8496..5b5cafa96562 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1673,7 +1673,7 @@ options! { print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "print the LLVM optimization passes being run (default: no)"), #[rustc_lint_opt_deny_field_access("use `Session::print_llvm_stats` instead of this field")] - print_llvm_stats: bool = (true, parse_bool, [UNTRACKED], + print_llvm_stats: bool = (false, parse_bool, [UNTRACKED], "print LLVM statistics (default: no)"), print_mono_items: Option = (None, parse_opt_string, [UNTRACKED], "print the result of the monomorphization collection pass"), From 4d307c482271ea3575a13b6c04222911e7706189 Mon Sep 17 00:00:00 2001 From: khei4 Date: Mon, 17 Jul 2023 00:37:52 +0900 Subject: [PATCH 009/113] print on rustc_codegen_llvm and rename malloc and cpy c_char --- compiler/rustc_codegen_llvm/src/lib.rs | 30 +++++++++++++++---- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 +-- compiler/rustc_interface/src/tests.rs | 2 +- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 22 ++++++++++---- compiler/rustc_session/src/options.rs | 6 ++-- compiler/rustc_session/src/session.rs | 2 +- 6 files changed, 47 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 713c22ebfeba..c03b21888244 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -177,14 +177,32 @@ impl WriteBackendMethods for LlvmCodegenBackend { type ThinData = back::lto::ThinData; type ThinBuffer = back::lto::ThinBuffer; fn print_pass_timings(&self) { - unsafe { - llvm::LLVMRustPrintPassTimings(); - } + let msg = unsafe { + let cstr = llvm::LLVMRustPrintPassTimings(); + if cstr.is_null() { + "failed to get pass timings".into() + } else { + let timings = CStr::from_ptr(cstr).to_bytes(); + let timings = String::from_utf8_lossy(timings).to_string(); + libc::free(cstr as *mut _); + timings + } + }; + println!("{}", msg); } fn print_statistics(&self) { - unsafe { - llvm::LLVMRustPrintStatistics(); - } + let msg = unsafe { + let cstr = llvm::LLVMRustPrintStatistics(); + if cstr.is_null() { + "failed to get stats".into() + } else { + let stats = CStr::from_ptr(cstr).to_bytes(); + let stats = String::from_utf8_lossy(stats).to_string(); + libc::free(cstr as *mut _); + stats + } + }; + println!("{}", msg); } fn run_link( cgcx: &CodegenContext, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 3eb045557497..7cc79d859a38 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1868,10 +1868,10 @@ extern "C" { pub fn LLVMRustGetLastError() -> *const c_char; /// Print the pass timings since static dtors aren't picking them up. - pub fn LLVMRustPrintPassTimings(); + pub fn LLVMRustPrintPassTimings() -> *const c_char; /// Print the statistics since static dtors aren't picking them up. - pub fn LLVMRustPrintStatistics(); + pub fn LLVMRustPrintStatistics() -> *const c_char; pub fn LLVMStructCreateNamed(C: &Context, Name: *const c_char) -> &Type; diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0eac098e8a3e..9aee39962df4 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -714,8 +714,8 @@ fn test_unstable_options_tracking_hash() { untracked!(perf_stats, true); // `pre_link_arg` is omitted because it just forwards to `pre_link_args`. untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]); + untracked!(print_codegen_stats, true); untracked!(print_llvm_passes, true); - untracked!(print_llvm_stats, true); untracked!(print_mono_items, Some(String::from("abc"))); untracked!(print_type_sizes, true); untracked!(proc_macro_backtrace, true); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 89beb09db755..695b8847a976 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -112,14 +112,24 @@ extern "C" void LLVMRustSetNormalizedTarget(LLVMModuleRef M, unwrap(M)->setTargetTriple(Triple::normalize(Triple)); } -extern "C" void LLVMRustPrintPassTimings() { - raw_fd_ostream OS(2, false); // stderr. - TimerGroup::printAll(OS); +extern "C" const char *LLVMRustPrintPassTimings(void) { + std::string buf; + raw_string_ostream SS(buf); + TimerGroup::printAll(SS); + SS.flush(); + char* CStr = (char*) malloc((buf.length() + 1) * sizeof(char)); + strcpy(CStr, buf.c_str()); + return CStr; } -extern "C" void LLVMRustPrintStatistics() { - raw_fd_ostream OS(2, false); // stderr. - llvm::PrintStatistics(OS); +extern "C" const char *LLVMRustPrintStatistics(void) { + std::string buf; + raw_string_ostream SS(buf); + llvm::PrintStatistics(SS); + SS.flush(); + char* CStr = (char*) malloc((buf.length() + 1) * sizeof(char)); + strcpy(CStr, buf.c_str()); + return CStr; } extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 5b5cafa96562..39efe9abeecd 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1668,13 +1668,13 @@ options! { "use a more precise version of drop elaboration for matches on enums (default: yes). \ This results in better codegen, but has caused miscompilations on some tier 2 platforms. \ See #77382 and #74551."), + #[rustc_lint_opt_deny_field_access("use `Session::print_codegen_stats` instead of this field")] + print_codegen_stats: bool = (false, parse_bool, [UNTRACKED], + "print codegen statistics (default: no)"), print_fuel: Option = (None, parse_opt_string, [TRACKED], "make rustc print the total optimization fuel used by a crate"), print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "print the LLVM optimization passes being run (default: no)"), - #[rustc_lint_opt_deny_field_access("use `Session::print_llvm_stats` instead of this field")] - print_llvm_stats: bool = (false, parse_bool, [UNTRACKED], - "print LLVM statistics (default: no)"), print_mono_items: Option = (None, parse_opt_string, [UNTRACKED], "print the result of the monomorphization collection pass"), print_type_sizes: bool = (false, parse_bool, [UNTRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index c2588b9a99a1..6ebba596a943 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1058,7 +1058,7 @@ impl Session { } pub fn print_llvm_stats(&self) -> bool { - self.opts.unstable_opts.print_llvm_stats + self.opts.unstable_opts.print_codegen_stats } pub fn verify_llvm_ir(&self) -> bool { From a4a5e5b4ae2431c09b228bfb3e2930a1c38b9e45 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 30 Jan 2022 17:14:54 +0100 Subject: [PATCH 010/113] Querify unused trait check. --- compiler/rustc_hir_analysis/src/check_unused.rs | 9 +++++++-- compiler/rustc_hir_analysis/src/lib.rs | 3 ++- compiler/rustc_middle/src/query/mod.rs | 4 ++++ tests/incremental/hashes/trait_defs.rs | 4 ++-- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs index 5318e637fc77..d10bc5b34ea9 100644 --- a/compiler/rustc_hir_analysis/src/check_unused.rs +++ b/compiler/rustc_hir_analysis/src/check_unused.rs @@ -1,11 +1,16 @@ use rustc_data_structures::unord::{ExtendUnord, UnordSet}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint; -pub fn check_crate(tcx: TyCtxt<'_>) { - let mut used_trait_imports: UnordSet = Default::default(); +pub fn provide(providers: &mut Providers) { + *providers = Providers { check_unused_traits, ..*providers }; +} + +fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) { + let mut used_trait_imports = UnordSet::::default(); // FIXME: Use `tcx.hir().par_body_owners()` when we implement creating `DefId`s // for anon constants during their parents' typeck. diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index d7e62457f36e..4f95174f869e 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -177,6 +177,7 @@ pub fn provide(providers: &mut Providers) { collect::provide(providers); coherence::provide(providers); check::provide(providers); + check_unused::provide(providers); variance::provide(providers); outlives::provide(providers); impl_wf_check::provide(providers); @@ -247,7 +248,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { } }); - check_unused::check_crate(tcx); + tcx.ensure().check_unused_traits(()); if let Some(reported) = tcx.sess.has_errors() { Err(reported) } else { Ok(()) } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 45fa82ba68ad..8f5f92f80d83 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -898,6 +898,10 @@ rustc_queries! { desc { |tcx| "linting {}", describe_as_module(key, tcx) } } + query check_unused_traits(_: ()) -> () { + desc { "checking unused trait imports in crate" } + } + /// Checks the attributes in the module. query check_mod_attrs(key: LocalDefId) -> () { desc { |tcx| "checking attributes in {}", describe_as_module(key, tcx) } diff --git a/tests/incremental/hashes/trait_defs.rs b/tests/incremental/hashes/trait_defs.rs index b583bee2f244..7b8c6245d2d4 100644 --- a/tests/incremental/hashes/trait_defs.rs +++ b/tests/incremental/hashes/trait_defs.rs @@ -420,13 +420,13 @@ trait TraitAddExternModifier { // ------------------------- // -------------------------------------------------------------------- // ------------------------- - fn method() ; + fn method(); } #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(except="hir_owner", cfg="cfail5")] +#[rustc_clean(cfg="cfail5")] #[rustc_clean(cfg="cfail6")] trait TraitAddExternModifier { #[rustc_clean(except="hir_owner,hir_owner_nodes,fn_sig", cfg="cfail2")] From b1398cac9d529fb818b0fdde44255e99fa63641e Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Tue, 18 Jul 2023 09:56:55 +0900 Subject: [PATCH 011/113] Make {Rc,Arc}::allocator associated functions --- library/alloc/src/rc.rs | 8 ++++++-- library/alloc/src/sync.rs | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index b3ec830a7d7c..bf01b2082eda 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -661,10 +661,14 @@ impl Rc { impl Rc { /// Returns a reference to the underlying allocator. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Rc::allocator(&r)` instead of `r.allocator()`. This + /// is so that there is no conflict with a method on the inner type. #[inline] #[unstable(feature = "allocator_api", issue = "32838")] - pub fn allocator(&self) -> &A { - &self.alloc + pub fn allocator(this: &Self) -> &A { + &this.alloc } /// Constructs a new `Rc` in the provided allocator. /// diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index e00850eb5d8a..c2202f2fce55 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -678,10 +678,14 @@ impl Arc { impl Arc { /// Returns a reference to the underlying allocator. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Arc::allocator(&a)` instead of `a.allocator()`. This + /// is so that there is no conflict with a method on the inner type. #[inline] #[unstable(feature = "allocator_api", issue = "32838")] - pub fn allocator(&self) -> &A { - &self.alloc + pub fn allocator(this: &Self) -> &A { + &this.alloc } /// Constructs a new `Arc` in the provided allocator. /// From c3462a0676429c20b1273b615e144a40025d495b Mon Sep 17 00:00:00 2001 From: KaDiWa Date: Thu, 28 Jul 2022 23:47:17 +0200 Subject: [PATCH 012/113] remove the unstable `core::sync::atomic::ATOMIC_*_INIT` constants --- library/core/src/sync/atomic.rs | 85 ++++++++++++++------------------- library/core/tests/lib.rs | 2 +- 2 files changed, 36 insertions(+), 51 deletions(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 236b7f423d6b..092b097c3961 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1958,14 +1958,12 @@ macro_rules! atomic_int { $stable_from:meta, $stable_nand:meta, $const_stable:meta, - $stable_init_const:meta, $diagnostic_item:meta, $s_int_type:literal, $extra_feature:expr, $min_fn:ident, $max_fn:ident, $align:expr, - $atomic_new:expr, - $int_type:ident $atomic_type:ident $atomic_init:ident) => { + $int_type:ident $atomic_type:ident) => { /// An integer type which can be safely shared between threads. /// /// This type has the same in-memory representation as the underlying @@ -1988,15 +1986,6 @@ macro_rules! atomic_int { v: UnsafeCell<$int_type>, } - /// An atomic integer initialized to `0`. - #[$stable_init_const] - #[deprecated( - since = "1.34.0", - note = "the `new` function is now preferred", - suggestion = $atomic_new, - )] - pub const $atomic_init: $atomic_type = $atomic_type::new(0); - #[$stable] impl Default for $atomic_type { #[inline] @@ -2874,14 +2863,12 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - unstable(feature = "integer_atomics", issue = "99069"), cfg_attr(not(test), rustc_diagnostic_item = "AtomicI8"), "i8", "", atomic_min, atomic_max, 1, - "AtomicI8::new(0)", - i8 AtomicI8 ATOMIC_I8_INIT + i8 AtomicI8 } #[cfg(target_has_atomic_load_store = "8")] atomic_int! { @@ -2894,14 +2881,12 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - unstable(feature = "integer_atomics", issue = "99069"), cfg_attr(not(test), rustc_diagnostic_item = "AtomicU8"), "u8", "", atomic_umin, atomic_umax, 1, - "AtomicU8::new(0)", - u8 AtomicU8 ATOMIC_U8_INIT + u8 AtomicU8 } #[cfg(target_has_atomic_load_store = "16")] atomic_int! { @@ -2914,14 +2899,12 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - unstable(feature = "integer_atomics", issue = "99069"), cfg_attr(not(test), rustc_diagnostic_item = "AtomicI16"), "i16", "", atomic_min, atomic_max, 2, - "AtomicI16::new(0)", - i16 AtomicI16 ATOMIC_I16_INIT + i16 AtomicI16 } #[cfg(target_has_atomic_load_store = "16")] atomic_int! { @@ -2934,14 +2917,12 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - unstable(feature = "integer_atomics", issue = "99069"), cfg_attr(not(test), rustc_diagnostic_item = "AtomicU16"), "u16", "", atomic_umin, atomic_umax, 2, - "AtomicU16::new(0)", - u16 AtomicU16 ATOMIC_U16_INIT + u16 AtomicU16 } #[cfg(target_has_atomic_load_store = "32")] atomic_int! { @@ -2954,14 +2935,12 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - unstable(feature = "integer_atomics", issue = "99069"), cfg_attr(not(test), rustc_diagnostic_item = "AtomicI32"), "i32", "", atomic_min, atomic_max, 4, - "AtomicI32::new(0)", - i32 AtomicI32 ATOMIC_I32_INIT + i32 AtomicI32 } #[cfg(target_has_atomic_load_store = "32")] atomic_int! { @@ -2974,14 +2953,12 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - unstable(feature = "integer_atomics", issue = "99069"), cfg_attr(not(test), rustc_diagnostic_item = "AtomicU32"), "u32", "", atomic_umin, atomic_umax, 4, - "AtomicU32::new(0)", - u32 AtomicU32 ATOMIC_U32_INIT + u32 AtomicU32 } #[cfg(target_has_atomic_load_store = "64")] atomic_int! { @@ -2994,14 +2971,12 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - unstable(feature = "integer_atomics", issue = "99069"), cfg_attr(not(test), rustc_diagnostic_item = "AtomicI64"), "i64", "", atomic_min, atomic_max, 8, - "AtomicI64::new(0)", - i64 AtomicI64 ATOMIC_I64_INIT + i64 AtomicI64 } #[cfg(target_has_atomic_load_store = "64")] atomic_int! { @@ -3014,14 +2989,12 @@ atomic_int! { stable(feature = "integer_atomics_stable", since = "1.34.0"), stable(feature = "integer_atomics_stable", since = "1.34.0"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - unstable(feature = "integer_atomics", issue = "99069"), cfg_attr(not(test), rustc_diagnostic_item = "AtomicU64"), "u64", "", atomic_umin, atomic_umax, 8, - "AtomicU64::new(0)", - u64 AtomicU64 ATOMIC_U64_INIT + u64 AtomicU64 } #[cfg(target_has_atomic_load_store = "128")] atomic_int! { @@ -3034,14 +3007,12 @@ atomic_int! { unstable(feature = "integer_atomics", issue = "99069"), unstable(feature = "integer_atomics", issue = "99069"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - unstable(feature = "integer_atomics", issue = "99069"), cfg_attr(not(test), rustc_diagnostic_item = "AtomicI128"), "i128", "#![feature(integer_atomics)]\n\n", atomic_min, atomic_max, 16, - "AtomicI128::new(0)", - i128 AtomicI128 ATOMIC_I128_INIT + i128 AtomicI128 } #[cfg(target_has_atomic_load_store = "128")] atomic_int! { @@ -3054,19 +3025,17 @@ atomic_int! { unstable(feature = "integer_atomics", issue = "99069"), unstable(feature = "integer_atomics", issue = "99069"), rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - unstable(feature = "integer_atomics", issue = "99069"), cfg_attr(not(test), rustc_diagnostic_item = "AtomicU128"), "u128", "#![feature(integer_atomics)]\n\n", atomic_umin, atomic_umax, 16, - "AtomicU128::new(0)", - u128 AtomicU128 ATOMIC_U128_INIT + u128 AtomicU128 } +#[cfg(target_has_atomic_load_store = "ptr")] macro_rules! atomic_int_ptr_sized { ( $($target_pointer_width:literal $align:literal)* ) => { $( - #[cfg(target_has_atomic_load_store = "ptr")] #[cfg(target_pointer_width = $target_pointer_width)] atomic_int! { cfg(target_has_atomic = "ptr"), @@ -3078,16 +3047,13 @@ macro_rules! atomic_int_ptr_sized { stable(feature = "atomic_from", since = "1.23.0"), stable(feature = "atomic_nand", since = "1.27.0"), rustc_const_stable(feature = "const_ptr_sized_atomics", since = "1.24.0"), - stable(feature = "rust1", since = "1.0.0"), cfg_attr(not(test), rustc_diagnostic_item = "AtomicIsize"), "isize", "", atomic_min, atomic_max, $align, - "AtomicIsize::new(0)", - isize AtomicIsize ATOMIC_ISIZE_INIT + isize AtomicIsize } - #[cfg(target_has_atomic_load_store = "ptr")] #[cfg(target_pointer_width = $target_pointer_width)] atomic_int! { cfg(target_has_atomic = "ptr"), @@ -3099,18 +3065,37 @@ macro_rules! atomic_int_ptr_sized { stable(feature = "atomic_from", since = "1.23.0"), stable(feature = "atomic_nand", since = "1.27.0"), rustc_const_stable(feature = "const_ptr_sized_atomics", since = "1.24.0"), - stable(feature = "rust1", since = "1.0.0"), cfg_attr(not(test), rustc_diagnostic_item = "AtomicUsize"), "usize", "", atomic_umin, atomic_umax, $align, - "AtomicUsize::new(0)", - usize AtomicUsize ATOMIC_USIZE_INIT + usize AtomicUsize } + + /// An [`AtomicIsize`] initialized to `0`. + #[cfg(target_pointer_width = $target_pointer_width)] + #[stable(feature = "rust1", since = "1.0.0")] + #[deprecated( + since = "1.34.0", + note = "the `new` function is now preferred", + suggestion = "AtomicIsize::new(0)", + )] + pub const ATOMIC_ISIZE_INIT: AtomicIsize = AtomicIsize::new(0); + + /// An [`AtomicUsize`] initialized to `0`. + #[cfg(target_pointer_width = $target_pointer_width)] + #[stable(feature = "rust1", since = "1.0.0")] + #[deprecated( + since = "1.34.0", + note = "the `new` function is now preferred", + suggestion = "AtomicUsize::new(0)", + )] + pub const ATOMIC_USIZE_INIT: AtomicUsize = AtomicUsize::new(0); )* }; } +#[cfg(target_has_atomic_load_store = "ptr")] atomic_int_ptr_sized! { "16" 2 "32" 4 diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 3e6d31fcd2fb..897a5e9b8706 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -93,7 +93,7 @@ #![feature(const_option)] #![feature(const_option_ext)] #![feature(const_result)] -#![feature(integer_atomics)] +#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![feature(int_roundings)] #![feature(slice_group_by)] #![feature(split_array)] From 9e5a67e57f715ec5eda915db1261cdf4c4a04642 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 10 Jul 2023 09:33:34 +0000 Subject: [PATCH 013/113] Permit pre-evaluated constants in simd_shuffle --- .../rustc_codegen_ssa/src/mir/constant.rs | 14 ++++++++++++++ tests/ui/simd/shuffle.rs | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 1c5031dfc4b4..47a166e4e1be 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -67,6 +67,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) -> Result>, ErrorHandled> { let uv = match constant.literal { mir::ConstantKind::Unevaluated(uv, _) => uv.shrink(), + mir::ConstantKind::Ty(c) => match c.kind() { + // A constant that came from a const generic but was then used as an argument to old-style + // simd_shuffle (passing as argument instead of as a generic param). + rustc_type_ir::ConstKind::Value(valtree) => return Ok(Some(valtree)), + other => span_bug!(constant.span, "{other:#?}"), + }, + // We should never encounter `ConstantKind::Val` unless MIR opts (like const prop) evaluate + // a constant and write that value back into `Operand`s. This could happen, but is unlikely. + // Also: all users of `simd_shuffle` are on unstable and already need to take a lot of care + // around intrinsics. For an issue to happen here, it would require a macro expanding to a + // `simd_shuffle` call without wrapping the constant argument in a `const {}` block, but + // the user pass through arbitrary expressions. + // FIXME(oli-obk): replace the magic const generic argument of `simd_shuffle` with a real + // const generic. other => span_bug!(constant.span, "{other:#?}"), }; let uv = self.monomorphize(uv); diff --git a/tests/ui/simd/shuffle.rs b/tests/ui/simd/shuffle.rs index 3592adfdc6ad..de41c9e25ddd 100644 --- a/tests/ui/simd/shuffle.rs +++ b/tests/ui/simd/shuffle.rs @@ -1,14 +1,21 @@ //run-pass #![feature(repr_simd, platform_intrinsics)] +#![allow(incomplete_features)] +#![feature(adt_const_params)] extern "platform-intrinsic" { fn simd_shuffle(a: T, b: T, i: I) -> U; + fn simd_shuffle16(x: T, y: T, idx: [u32; 16]) -> U; } #[derive(Copy, Clone)] #[repr(simd)] struct Simd([T; N]); +pub unsafe fn __shuffle_vector16(x: T, y: T) -> U { + simd_shuffle16(x, y, IDX) +} + fn main() { const I1: [u32; 4] = [0, 2, 4, 6]; const I2: [u32; 2] = [1, 5]; @@ -21,4 +28,16 @@ fn main() { let y: Simd = simd_shuffle(a, b, I2); assert_eq!(y.0, [1, 5]); } + // Test that an indirection (via an unnamed constant) + // through a const generic parameter also works. + // See https://github.com/rust-lang/rust/issues/113500 for details. + let a = Simd::([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + let b = Simd::([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]); + unsafe { + __shuffle_vector16::< + { [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] }, + Simd, + Simd, + >(a, b); + } } From 9b32319205dd207c9bdb501ee353a542fc10674d Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 18 Jul 2023 11:18:40 -0300 Subject: [PATCH 014/113] Add Foreign to SMIR --- compiler/rustc_smir/src/rustc_internal/mod.rs | 8 ++++++++ compiler/rustc_smir/src/rustc_smir/mod.rs | 4 +++- compiler/rustc_smir/src/stable_mir/ty.rs | 4 ++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 527d5220564c..2ac3046ce6d7 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -31,6 +31,10 @@ pub fn adt_def(did: DefId) -> stable_mir::ty::AdtDef { with_tables(|t| t.adt_def(did)) } +pub fn foreign_def(did: DefId) -> stable_mir::ty::ForeignDef { + with_tables(|t| t.foreign_def(did)) +} + impl<'tcx> Tables<'tcx> { pub fn item_def_id(&self, item: &stable_mir::CrateItem) -> DefId { self.def_ids[item.0] @@ -44,6 +48,10 @@ impl<'tcx> Tables<'tcx> { stable_mir::ty::AdtDef(self.create_def_id(did)) } + pub fn foreign_def(&mut self, did: DefId) -> stable_mir::ty::ForeignDef { + stable_mir::ty::ForeignDef(self.create_def_id(did)) + } + fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId { // FIXME: this becomes inefficient when we have too many ids for (i, &d) in self.def_ids.iter().enumerate() { diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 0e5de1e74d32..13bda35d6927 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -113,7 +113,9 @@ impl<'tcx> Tables<'tcx> { .collect(), ), )), - ty::Foreign(_) => todo!(), + ty::Foreign(def_id) => { + TyKind::RigidTy(RigidTy::Foreign(rustc_internal::foreign_def(*def_id))) + } ty::Str => TyKind::RigidTy(RigidTy::Str), ty::Array(ty, constant) => { TyKind::RigidTy(RigidTy::Array(self.intern_ty(*ty), opaque(constant))) diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 7ae07efb729d..162dfe05becd 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -26,6 +26,7 @@ pub enum RigidTy { Uint(UintTy), Float(FloatTy), Adt(AdtDef, AdtSubsts), + Foreign(ForeignDef), Str, Array(Ty, Const), Slice(Ty), @@ -60,6 +61,9 @@ pub enum FloatTy { F64, } +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct ForeignDef(pub(crate) DefId); + #[derive(Clone, PartialEq, Eq, Debug)] pub struct AdtDef(pub(crate) DefId); From caa01adbd061b17e97ada679e87a96da713310fa Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 18 Jul 2023 12:02:35 -0300 Subject: [PATCH 015/113] Add Never to SMIR --- compiler/rustc_smir/src/rustc_smir/mod.rs | 2 +- compiler/rustc_smir/src/stable_mir/ty.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 13bda35d6927..922bbf029387 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -132,7 +132,7 @@ impl<'tcx> Tables<'tcx> { ty::Dynamic(_, _, _) => todo!(), ty::Closure(_, _) => todo!(), ty::Generator(_, _, _) => todo!(), - ty::Never => todo!(), + ty::Never => TyKind::RigidTy(RigidTy::Never), ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple( fields.iter().map(|ty| self.intern_ty(ty)).collect(), )), diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 162dfe05becd..571fd0d74724 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -32,6 +32,7 @@ pub enum RigidTy { Slice(Ty), RawPtr(Ty, Mutability), Ref(Region, Ty, Mutability), + Never, Tuple(Vec), } From c6f0a7c3c3070f90a9e9dcc3e5a91d1183afc978 Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Wed, 19 Jul 2023 11:19:33 +0800 Subject: [PATCH 016/113] avoid clone path prefix when lowering to hir Signed-off-by: SparrowLii --- compiler/rustc_ast_lowering/src/item.rs | 11 ----------- compiler/rustc_ast_lowering/src/lib.rs | 10 ---------- 2 files changed, 21 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index ab68436c0939..d3fc76db1fa7 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -551,17 +551,6 @@ impl<'hir> LoweringContext<'_, 'hir> { for &(ref use_tree, id) in trees { let new_hir_id = self.local_def_id(id); - let mut prefix = prefix.clone(); - - // Give the segments new node-ids since they are being cloned. - for seg in &mut prefix.segments { - // Give the cloned segment the same resolution information - // as the old one (this is needed for stability checking). - let new_id = self.next_node_id(); - self.resolver.clone_res(seg.id, new_id); - seg.id = new_id; - } - // Each `use` import is an item and thus are owners of the // names in the path. Up to this point the nested import is // the current owner, since we want each desugared import to diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 429e62c4a1c6..563577067456 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -148,10 +148,6 @@ trait ResolverAstLoweringExt { fn legacy_const_generic_args(&self, expr: &Expr) -> Option>; fn get_partial_res(&self, id: NodeId) -> Option; fn get_import_res(&self, id: NodeId) -> PerNS>>; - // Clones the resolution (if any) on 'source' and applies it - // to 'target'. Used when desugaring a `UseTreeKind::Nested` to - // multiple `UseTreeKind::Simple`s - fn clone_res(&mut self, source: NodeId, target: NodeId); fn get_label_res(&self, id: NodeId) -> Option; fn get_lifetime_res(&self, id: NodeId) -> Option; fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)>; @@ -184,12 +180,6 @@ impl ResolverAstLoweringExt for ResolverAstLowering { None } - fn clone_res(&mut self, source: NodeId, target: NodeId) { - if let Some(res) = self.partial_res_map.get(&source) { - self.partial_res_map.insert(target, *res); - } - } - /// Obtains resolution for a `NodeId` with a single resolution. fn get_partial_res(&self, id: NodeId) -> Option { self.partial_res_map.get(&id).copied() From 0377945157da71c05ba52edca2de6f071212338c Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Wed, 19 Jul 2023 16:48:33 +0800 Subject: [PATCH 017/113] add comment for lower_use_tree Signed-off-by: SparrowLii --- compiler/rustc_ast_lowering/src/item.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index d3fc76db1fa7..2f58f566c81c 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -559,6 +559,9 @@ impl<'hir> LoweringContext<'_, 'hir> { self.with_hir_id_owner(id, |this| { let mut ident = *ident; + // `prefix` is lowered multiple times, but in different HIR owners. + // So each segment gets renewed `HirId` with the same + // `ItemLocalId` and the new owner. (See `lower_node_id`) let kind = this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs); if let Some(attrs) = attrs { From eabd306265911c30641dbf8b263e0191a7e07f9a Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 19 Jul 2023 09:44:40 +0000 Subject: [PATCH 018/113] Document `PredicateSet::insert` I always forget what the `bool` means :/ --- compiler/rustc_infer/src/traits/util.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 074ff7ec97f8..87ba6b3ec508 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -25,6 +25,13 @@ impl<'tcx> PredicateSet<'tcx> { Self { tcx, set: Default::default() } } + /// Adds a predicate to the set. + /// + /// Returns whether the predicate was newly inserted. That is: + /// - If the set did not previously contain this predicate, `true` is returned. + /// - If the set already contained this predicate, `false` is returned, + /// and the set is not modified: original predicate is not replaced, + /// and the predicate passed as argument is dropped. pub fn insert(&mut self, pred: ty::Predicate<'tcx>) -> bool { // We have to be careful here because we want // From 348f26e40957c2a2469f0ea2b41c403803de8075 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 19 Jul 2023 09:45:26 +0000 Subject: [PATCH 019/113] Use `?` in `prepare_vtable_segments` --- .../src/traits/vtable.rs | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 78816fed0173..0a3f423d3c1a 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -24,8 +24,18 @@ pub enum VtblSegment<'tcx> { pub fn prepare_vtable_segments<'tcx, T>( tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, - mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow, + segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow, ) -> Option { + prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value() +} + +/// Helper for [`prepare_vtable_segments`] that returns `ControlFlow`, +/// such that we can use `?` in the body. +fn prepare_vtable_segments_inner<'tcx, T>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow, +) -> ControlFlow { // The following constraints holds for the final arrangement. // 1. The whole virtual table of the first direct super trait is included as the // the prefix. If this trait doesn't have any super traits, then this step @@ -71,9 +81,7 @@ pub fn prepare_vtable_segments<'tcx, T>( // N, N-vptr, O // emit dsa segment first. - if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::MetadataDSA) { - return Some(v); - } + segment_visitor(VtblSegment::MetadataDSA)?; let mut emit_vptr_on_new_entry = false; let mut visited = PredicateSet::new(tcx); @@ -146,12 +154,10 @@ pub fn prepare_vtable_segments<'tcx, T>( // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. 'exiting_out: loop { if let Some((inner_most_trait_ref, emit_vptr, siblings_opt)) = stack.last_mut() { - if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::TraitOwnEntries { + segment_visitor(VtblSegment::TraitOwnEntries { trait_ref: *inner_most_trait_ref, emit_vptr: *emit_vptr, - }) { - return Some(v); - } + })?; 'exiting_out_skip_visited_traits: loop { if let Some(siblings) = siblings_opt { @@ -174,7 +180,7 @@ pub fn prepare_vtable_segments<'tcx, T>( } } // all done - return None; + return ControlFlow::Continue(()); } } } From f8f5d7aab28b9c310b4312b5d38c80fe4dc6df02 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 19 Jul 2023 09:49:13 +0000 Subject: [PATCH 020/113] Replace `if let` with `unwrap` in `prepare_vtable_segments` Reasoning: if the stack is empty, the loop will be infinite, so the assumption is that the stack can't be non empty. Unwrap makes the assumption more clear (and removes an indentation level) --- .../src/traits/vtable.rs | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 0a3f423d3c1a..ac9fb51e3919 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -116,34 +116,33 @@ fn prepare_vtable_segments_inner<'tcx, T>( loop { // dive deeper into the stack, recording the path 'diving_in: loop { - if let Some((inner_most_trait_ref, _, _)) = stack.last() { - let inner_most_trait_ref = *inner_most_trait_ref; - let mut direct_super_traits_iter = tcx - .super_predicates_of(inner_most_trait_ref.def_id()) - .predicates - .into_iter() - .filter_map(move |(pred, _)| { - pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause() - }); + let &(inner_most_trait_ref, _, _) = stack.last().unwrap(); - 'diving_in_skip_visited_traits: loop { - if let Some(next_super_trait) = direct_super_traits_iter.next() { - if visited.insert(next_super_trait.to_predicate(tcx)) { - // We're throwing away potential constness of super traits here. - // FIXME: handle ~const super traits - let next_super_trait = next_super_trait.map_bound(|t| t.trait_ref); - stack.push(( - next_super_trait, - emit_vptr_on_new_entry, - Some(direct_super_traits_iter), - )); - break 'diving_in_skip_visited_traits; - } else { - continue 'diving_in_skip_visited_traits; - } + let mut direct_super_traits_iter = tcx + .super_predicates_of(inner_most_trait_ref.def_id()) + .predicates + .into_iter() + .filter_map(move |(pred, _)| { + pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause() + }); + + 'diving_in_skip_visited_traits: loop { + if let Some(next_super_trait) = direct_super_traits_iter.next() { + if visited.insert(next_super_trait.to_predicate(tcx)) { + // We're throwing away potential constness of super traits here. + // FIXME: handle ~const super traits + let next_super_trait = next_super_trait.map_bound(|t| t.trait_ref); + stack.push(( + next_super_trait, + emit_vptr_on_new_entry, + Some(direct_super_traits_iter), + )); + break 'diving_in_skip_visited_traits; } else { - break 'diving_in; + continue 'diving_in_skip_visited_traits; } + } else { + break 'diving_in; } } } From f5feb3e3ca739d0f2eeef6e914f97316a05af01d Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 16 Jul 2023 13:11:10 +0000 Subject: [PATCH 021/113] Turn copy into moves during DSE. --- .../rustc_mir_dataflow/src/impls/liveness.rs | 2 +- compiler/rustc_mir_dataflow/src/impls/mod.rs | 1 + .../src/dead_store_elimination.rs | 40 ++++++++++++++++++- tests/codegen/iter-repeat-n-trivial-drop.rs | 2 +- tests/codegen/move-operands.rs | 2 +- ...mple.DeadStoreElimination.panic-abort.diff | 23 +++++++++++ ...ple.DeadStoreElimination.panic-unwind.diff | 23 +++++++++++ .../dead-store-elimination/call_arg_copy.rs | 15 +++++++ ...yn_trait.get_query.Inline.panic-abort.diff | 2 +- ...n_trait.get_query.Inline.panic-unwind.diff | 2 +- ....try_execute_query.Inline.panic-abort.diff | 2 +- ...try_execute_query.Inline.panic-unwind.diff | 2 +- ...nto_box_place.main.Inline.panic-abort.diff | 4 +- ...to_box_place.main.Inline.panic-unwind.diff | 4 +- ...ethod_2.test2.Inline.after.panic-abort.mir | 2 +- ...thod_2.test2.Inline.after.panic-unwind.mir | 2 +- ...ue_101973.inner.ConstProp.panic-abort.diff | 2 +- ...e_101973.inner.ConstProp.panic-unwind.diff | 2 +- ..._to_digit.PreCodegen.after.panic-abort.mir | 2 +- ...to_digit.PreCodegen.after.panic-unwind.mir | 2 +- ...ecked_ops.checked_shl.PreCodegen.after.mir | 2 +- .../loops.int_range.PreCodegen.after.mir | 2 +- ...sive_loop.PreCodegen.after.panic-abort.mir | 2 +- ...ive_loop.PreCodegen.after.panic-unwind.mir | 2 +- ...iter_next.PreCodegen.after.panic-abort.mir | 2 +- ...ter_next.PreCodegen.after.panic-unwind.mir | 2 +- ...dex_range.PreCodegen.after.panic-abort.mir | 2 +- ...ex_range.PreCodegen.after.panic-unwind.mir | 2 +- ...next_back.PreCodegen.after.panic-abort.mir | 2 +- ...ext_back.PreCodegen.after.panic-unwind.mir | 2 +- ...iter_next.PreCodegen.after.panic-abort.mir | 2 +- ...ter_next.PreCodegen.after.panic-unwind.mir | 2 +- 32 files changed, 129 insertions(+), 31 deletions(-) create mode 100644 tests/mir-opt/dead-store-elimination/call_arg_copy.move_simple.DeadStoreElimination.panic-abort.diff create mode 100644 tests/mir-opt/dead-store-elimination/call_arg_copy.move_simple.DeadStoreElimination.panic-unwind.diff create mode 100644 tests/mir-opt/dead-store-elimination/call_arg_copy.rs diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 9662c19777fe..34e0834a68b3 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -88,7 +88,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { } } -struct TransferFunction<'a, T>(&'a mut T); +pub struct TransferFunction<'a, T>(pub &'a mut T); impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T> where diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index 633b99a332bb..7ddd01e34aaa 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -26,6 +26,7 @@ pub use self::borrowed_locals::borrowed_locals; pub use self::borrowed_locals::MaybeBorrowedLocals; pub use self::liveness::MaybeLiveLocals; pub use self::liveness::MaybeTransitiveLiveLocals; +pub use self::liveness::TransferFunction as LivenessTransferFunction; pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive}; /// `MaybeInitializedPlaces` tracks all places that might be diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 7bc5183a00a8..3f988930b5e6 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -13,9 +13,12 @@ //! use rustc_index::bit_set::BitSet; +use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -use rustc_mir_dataflow::impls::{borrowed_locals, MaybeTransitiveLiveLocals}; +use rustc_mir_dataflow::impls::{ + borrowed_locals, LivenessTransferFunction, MaybeTransitiveLiveLocals, +}; use rustc_mir_dataflow::Analysis; /// Performs the optimization on the body @@ -28,8 +31,33 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS .iterate_to_fixpoint() .into_results_cursor(body); + // For blocks with a call terminator, if an argument copy can be turned into a move, + // record it as (block, argument index). + let mut call_operands_to_move = Vec::new(); let mut patch = Vec::new(); + for (bb, bb_data) in traversal::preorder(body) { + if let TerminatorKind::Call { ref args, .. } = bb_data.terminator().kind { + let loc = Location { block: bb, statement_index: bb_data.statements.len() }; + + // Position ourselves between the evaluation of `args` and the write to `destination`. + live.seek_to_block_end(bb); + let mut state = live.get().clone(); + + for (index, arg) in args.iter().enumerate().rev() { + if let Operand::Copy(place) = *arg + && !place.is_indirect() + && !borrowed.contains(place.local) + && !state.contains(place.local) + { + call_operands_to_move.push((bb, index)); + } + + // Account that `arg` is read from, so we don't promote another argument to a move. + LivenessTransferFunction(&mut state).visit_operand(arg, loc); + } + } + for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() { let loc = Location { block: bb, statement_index }; if let StatementKind::Assign(assign) = &statement.kind { @@ -64,7 +92,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS } } - if patch.is_empty() { + if patch.is_empty() && call_operands_to_move.is_empty() { return; } @@ -72,6 +100,14 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS for Location { block, statement_index } in patch { bbs[block].statements[statement_index].make_nop(); } + for (block, argument_index) in call_operands_to_move { + let TerminatorKind::Call { ref mut args, .. } = bbs[block].terminator_mut().kind else { + bug!() + }; + let arg = &mut args[argument_index]; + let Operand::Copy(place) = *arg else { bug!() }; + *arg = Operand::Move(place); + } crate::simplify::simplify_locals(body, tcx) } diff --git a/tests/codegen/iter-repeat-n-trivial-drop.rs b/tests/codegen/iter-repeat-n-trivial-drop.rs index 65a0f7e7ffb9..24059f190acf 100644 --- a/tests/codegen/iter-repeat-n-trivial-drop.rs +++ b/tests/codegen/iter-repeat-n-trivial-drop.rs @@ -33,7 +33,7 @@ pub fn iter_repeat_n_next(it: &mut std::iter::RepeatN) -> Option () { + debug x => _1; + let mut _0: (); + let _2: (); +- let mut _3: i32; +- let mut _4: i32; + + bb0: { + StorageLive(_2); +- _2 = use_both(_1, _1) -> [return: bb1, unwind unreachable]; ++ _2 = use_both(_1, move _1) -> [return: bb1, unwind unreachable]; + } + + bb1: { + StorageDead(_2); + _0 = const (); + return; + } + } + diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.move_simple.DeadStoreElimination.panic-unwind.diff b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_simple.DeadStoreElimination.panic-unwind.diff new file mode 100644 index 000000000000..0551d663b50f --- /dev/null +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_simple.DeadStoreElimination.panic-unwind.diff @@ -0,0 +1,23 @@ +- // MIR for `move_simple` before DeadStoreElimination ++ // MIR for `move_simple` after DeadStoreElimination + + fn move_simple(_1: i32) -> () { + debug x => _1; + let mut _0: (); + let _2: (); +- let mut _3: i32; +- let mut _4: i32; + + bb0: { + StorageLive(_2); +- _2 = use_both(_1, _1) -> [return: bb1, unwind continue]; ++ _2 = use_both(_1, move _1) -> [return: bb1, unwind continue]; + } + + bb1: { + StorageDead(_2); + _0 = const (); + return; + } + } + diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs new file mode 100644 index 000000000000..41f91fc13061 --- /dev/null +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs @@ -0,0 +1,15 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +// unit-test: DeadStoreElimination +// compile-flags: -Zmir-enable-passes=+CopyProp + +#[inline(never)] +fn use_both(_: i32, _: i32) {} + +// EMIT_MIR call_arg_copy.move_simple.DeadStoreElimination.diff +fn move_simple(x: i32) { + use_both(x, x); +} + +fn main() { + move_simple(1); +} diff --git a/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-abort.diff b/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-abort.diff index 57b0849e111d..9d5042caae2c 100644 --- a/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-abort.diff @@ -32,7 +32,7 @@ - _0 = try_execute_query::<::C>(move _4) -> [return: bb2, unwind unreachable]; + StorageLive(_5); + _5 = _4 as &dyn Cache::V> (PointerCoercion(Unsize)); -+ _0 = ::V> as Cache>::store_nocache(_5) -> [return: bb2, unwind unreachable]; ++ _0 = ::V> as Cache>::store_nocache(move _5) -> [return: bb2, unwind unreachable]; } bb2: { diff --git a/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-unwind.diff b/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-unwind.diff index 706c1d481950..9bd3855c58f8 100644 --- a/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/dyn_trait.get_query.Inline.panic-unwind.diff @@ -32,7 +32,7 @@ - _0 = try_execute_query::<::C>(move _4) -> [return: bb2, unwind continue]; + StorageLive(_5); + _5 = _4 as &dyn Cache::V> (PointerCoercion(Unsize)); -+ _0 = ::V> as Cache>::store_nocache(_5) -> [return: bb2, unwind continue]; ++ _0 = ::V> as Cache>::store_nocache(move _5) -> [return: bb2, unwind continue]; } bb2: { diff --git a/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-abort.diff b/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-abort.diff index 9a6d3596fb96..96e16d023ba1 100644 --- a/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-abort.diff @@ -17,7 +17,7 @@ _2 = move _3 as &dyn Cache::V> (PointerCoercion(Unsize)); StorageDead(_3); - _0 = mk_cycle::<::V>(move _2) -> [return: bb1, unwind unreachable]; -+ _0 = ::V> as Cache>::store_nocache(_2) -> [return: bb1, unwind unreachable]; ++ _0 = ::V> as Cache>::store_nocache(move _2) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-unwind.diff b/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-unwind.diff index 1a08df2b09b9..06d65abcbc12 100644 --- a/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/dyn_trait.try_execute_query.Inline.panic-unwind.diff @@ -17,7 +17,7 @@ _2 = move _3 as &dyn Cache::V> (PointerCoercion(Unsize)); StorageDead(_3); - _0 = mk_cycle::<::V>(move _2) -> [return: bb1, unwind continue]; -+ _0 = ::V> as Cache>::store_nocache(_2) -> [return: bb1, unwind continue]; ++ _0 = ::V> as Cache>::store_nocache(move _2) -> [return: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-abort.diff index dfc00026ad0c..dc0ab255afd9 100644 --- a/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-abort.diff @@ -134,7 +134,7 @@ + StorageDead(_14); + StorageLive(_9); + _13 = const _; -+ _9 = std::alloc::Global::alloc_impl(_13, _8, const false) -> [return: bb5, unwind unreachable]; ++ _9 = std::alloc::Global::alloc_impl(move _13, _8, const false) -> [return: bb5, unwind unreachable]; } bb1: { @@ -144,7 +144,7 @@ } bb2: { -+ _12 = handle_alloc_error(_8) -> unwind unreachable; ++ _12 = handle_alloc_error(move _8) -> unwind unreachable; + } + + bb3: { diff --git a/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-unwind.diff b/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-unwind.diff index f582adda88fd..54c33aac9e80 100644 --- a/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/inline_into_box_place.main.Inline.panic-unwind.diff @@ -134,7 +134,7 @@ + StorageDead(_14); + StorageLive(_9); + _13 = const _; -+ _9 = std::alloc::Global::alloc_impl(_13, _8, const false) -> [return: bb7, unwind: bb3]; ++ _9 = std::alloc::Global::alloc_impl(move _13, _8, const false) -> [return: bb7, unwind: bb3]; } bb1: { @@ -161,7 +161,7 @@ - bb4 (cleanup): { - resume; + bb4: { -+ _12 = handle_alloc_error(_8) -> bb3; ++ _12 = handle_alloc_error(move _8) -> bb3; + } + + bb5: { diff --git a/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-abort.mir b/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-abort.mir index 503f153089ce..f0d1cfe0359e 100644 --- a/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-abort.mir +++ b/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-abort.mir @@ -15,7 +15,7 @@ fn test2(_1: &dyn X) -> bool { _3 = &(*_1); _2 = move _3 as &dyn X (PointerCoercion(Unsize)); StorageDead(_3); - _0 = ::y(_2) -> [return: bb1, unwind unreachable]; + _0 = ::y(move _2) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-unwind.mir b/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-unwind.mir index 37bb53e79c64..f37b08143013 100644 --- a/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-unwind.mir +++ b/tests/mir-opt/inline/inline_trait_method_2.test2.Inline.after.panic-unwind.mir @@ -15,7 +15,7 @@ fn test2(_1: &dyn X) -> bool { _3 = &(*_1); _2 = move _3 as &dyn X (PointerCoercion(Unsize)); StorageDead(_3); - _0 = ::y(_2) -> [return: bb1, unwind continue]; + _0 = ::y(move _2) -> [return: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/issue_101973.inner.ConstProp.panic-abort.diff b/tests/mir-opt/issue_101973.inner.ConstProp.panic-abort.diff index e018563dbfee..ce490e894f03 100644 --- a/tests/mir-opt/issue_101973.inner.ConstProp.panic-abort.diff +++ b/tests/mir-opt/issue_101973.inner.ConstProp.panic-abort.diff @@ -66,7 +66,7 @@ bb2: { _6 = Shl(move _7, const 1_i32); StorageDead(_7); - _3 = rotate_right::(_4, _6) -> [return: bb3, unwind unreachable]; + _3 = rotate_right::(move _4, move _6) -> [return: bb3, unwind unreachable]; } bb3: { diff --git a/tests/mir-opt/issue_101973.inner.ConstProp.panic-unwind.diff b/tests/mir-opt/issue_101973.inner.ConstProp.panic-unwind.diff index a6bd29e1c9d1..254557b9947f 100644 --- a/tests/mir-opt/issue_101973.inner.ConstProp.panic-unwind.diff +++ b/tests/mir-opt/issue_101973.inner.ConstProp.panic-unwind.diff @@ -66,7 +66,7 @@ bb2: { _6 = Shl(move _7, const 1_i32); StorageDead(_7); - _3 = rotate_right::(_4, _6) -> [return: bb3, unwind unreachable]; + _3 = rotate_right::(move _4, move _6) -> [return: bb3, unwind unreachable]; } bb3: { diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir index 9743e1924628..a7a14ea645b0 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-abort.mir @@ -35,7 +35,7 @@ fn num_to_digit(_1: char) -> u32 { bb2: { StorageLive(_4); - _4 = char::methods::::to_digit(_1, const 8_u32) -> [return: bb3, unwind unreachable]; + _4 = char::methods::::to_digit(move _1, const 8_u32) -> [return: bb3, unwind unreachable]; } bb3: { diff --git a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir index e89d6eb4d50c..5f8c6f7283c5 100644 --- a/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.panic-unwind.mir @@ -35,7 +35,7 @@ fn num_to_digit(_1: char) -> u32 { bb2: { StorageLive(_4); - _4 = char::methods::::to_digit(_1, const 8_u32) -> [return: bb3, unwind continue]; + _4 = char::methods::::to_digit(move _1, const 8_u32) -> [return: bb3, unwind continue]; } bb3: { diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir index 800308c2e0bd..1228114eab87 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir @@ -46,7 +46,7 @@ fn checked_shl(_1: u32, _2: u32) -> Option { StorageDead(_4); _6 = Ge(_2, const _); StorageLive(_7); - _7 = unlikely(_6) -> [return: bb1, unwind unreachable]; + _7 = unlikely(move _6) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir index 99dc9600e41e..4c6bcd1bdbd5 100644 --- a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir @@ -88,7 +88,7 @@ fn int_range(_1: usize, _2: usize) -> () { bb7: { _10 = ((_6 as Some).0: usize); - _11 = opaque::(_10) -> [return: bb8, unwind continue]; + _11 = opaque::(move _10) -> [return: bb8, unwind continue]; } bb8: { diff --git a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-abort.mir index 8d306858b433..1b23e421368b 100644 --- a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-abort.mir @@ -42,7 +42,7 @@ fn inclusive_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { StorageLive(_7); StorageLive(_6); _6 = &mut _5; - _7 = as iter::range::RangeInclusiveIteratorImpl>::spec_next(_6) -> [return: bb2, unwind unreachable]; + _7 = as iter::range::RangeInclusiveIteratorImpl>::spec_next(move _6) -> [return: bb2, unwind unreachable]; } bb2: { diff --git a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-unwind.mir index e92c054c8383..bbab4e47a3ab 100644 --- a/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/range_iter.inclusive_loop.PreCodegen.after.panic-unwind.mir @@ -42,7 +42,7 @@ fn inclusive_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { StorageLive(_7); StorageLive(_6); _6 = &mut _5; - _7 = as iter::range::RangeInclusiveIteratorImpl>::spec_next(_6) -> [return: bb2, unwind: bb8]; + _7 = as iter::range::RangeInclusiveIteratorImpl>::spec_next(move _6) -> [return: bb2, unwind: bb8]; } bb2: { diff --git a/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-abort.mir index 0a71b6b2cf4a..b0f475b4db7f 100644 --- a/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-abort.mir @@ -8,7 +8,7 @@ fn range_inclusive_iter_next(_1: &mut RangeInclusive) -> Option { } bb0: { - _0 = as iter::range::RangeInclusiveIteratorImpl>::spec_next(_1) -> [return: bb1, unwind unreachable]; + _0 = as iter::range::RangeInclusiveIteratorImpl>::spec_next(move _1) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-unwind.mir index fd565fe75ec3..663ec229f723 100644 --- a/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/range_iter.range_inclusive_iter_next.PreCodegen.after.panic-unwind.mir @@ -8,7 +8,7 @@ fn range_inclusive_iter_next(_1: &mut RangeInclusive) -> Option { } bb0: { - _0 = as iter::range::RangeInclusiveIteratorImpl>::spec_next(_1) -> [return: bb1, unwind continue]; + _0 = as iter::range::RangeInclusiveIteratorImpl>::spec_next(move _1) -> [return: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir index aff718566ae1..df6d2263dc34 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-abort.mir @@ -12,7 +12,7 @@ fn slice_index_range(_1: &[u32], _2: std::ops::Range) -> &[u32] { bb0: { StorageLive(_3); - _3 = as SliceIndex<[u32]>>::index(move _2, _1) -> [return: bb1, unwind unreachable]; + _3 = as SliceIndex<[u32]>>::index(move _2, move _1) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir index a6b931d2c244..cc1795c3f977 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_index_range.PreCodegen.after.panic-unwind.mir @@ -12,7 +12,7 @@ fn slice_index_range(_1: &[u32], _2: std::ops::Range) -> &[u32] { bb0: { StorageLive(_3); - _3 = as SliceIndex<[u32]>>::index(move _2, _1) -> [return: bb1, unwind continue]; + _3 = as SliceIndex<[u32]>>::index(move _2, move _1) -> [return: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir index 0471d0757c7f..78f96bf41955 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-abort.mir @@ -5,7 +5,7 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut let mut _0: std::option::Option<&mut T>; bb0: { - _0 = as DoubleEndedIterator>::next_back(_1) -> [return: bb1, unwind unreachable]; + _0 = as DoubleEndedIterator>::next_back(move _1) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir index 386f3a9edcd6..dfe5e206fada 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_mut_next_back.PreCodegen.after.panic-unwind.mir @@ -5,7 +5,7 @@ fn slice_iter_mut_next_back(_1: &mut std::slice::IterMut<'_, T>) -> Option<&mut let mut _0: std::option::Option<&mut T>; bb0: { - _0 = as DoubleEndedIterator>::next_back(_1) -> [return: bb1, unwind continue]; + _0 = as DoubleEndedIterator>::next_back(move _1) -> [return: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir index 8c0209ae19bd..8edac638ccdd 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-abort.mir @@ -5,7 +5,7 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { let mut _0: std::option::Option<&T>; bb0: { - _0 = as Iterator>::next(_1) -> [return: bb1, unwind unreachable]; + _0 = as Iterator>::next(move _1) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir index e76ec00391cc..fdde07173437 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.slice_iter_next.PreCodegen.after.panic-unwind.mir @@ -5,7 +5,7 @@ fn slice_iter_next(_1: &mut std::slice::Iter<'_, T>) -> Option<&T> { let mut _0: std::option::Option<&T>; bb0: { - _0 = as Iterator>::next(_1) -> [return: bb1, unwind continue]; + _0 = as Iterator>::next(move _1) -> [return: bb1, unwind continue]; } bb1: { From 54a140159d91d769e0a202c4cee5d3bf233f9e1a Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 18 Jul 2023 18:18:17 +0000 Subject: [PATCH 022/113] Enable MIR opts for test. --- tests/codegen/move-operands.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/codegen/move-operands.rs b/tests/codegen/move-operands.rs index 3892740fdc14..df4fbe29ffdd 100644 --- a/tests/codegen/move-operands.rs +++ b/tests/codegen/move-operands.rs @@ -1,4 +1,5 @@ -// compile-flags: -C no-prepopulate-passes +// Verify that optimized MIR only copies `a` once. +// compile-flags: -O -C no-prepopulate-passes #![crate_type = "lib"] From 254bf6027d83a94e93b5112d558c81a3e050413f Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 19 Jul 2023 09:59:35 +0000 Subject: [PATCH 023/113] Make test order-independent. --- tests/codegen/iter-repeat-n-trivial-drop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen/iter-repeat-n-trivial-drop.rs b/tests/codegen/iter-repeat-n-trivial-drop.rs index 24059f190acf..68b10934a0d8 100644 --- a/tests/codegen/iter-repeat-n-trivial-drop.rs +++ b/tests/codegen/iter-repeat-n-trivial-drop.rs @@ -33,7 +33,7 @@ pub fn iter_repeat_n_next(it: &mut std::iter::RepeatN) -> Option Date: Wed, 19 Jul 2023 10:01:48 +0000 Subject: [PATCH 024/113] Refactor 'diving_in loop internals in `prepare_vtable_segments` Less explicit loops -- easier to read. --- .../src/traits/vtable.rs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index ac9fb51e3919..89b5272083ec 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -126,24 +126,24 @@ fn prepare_vtable_segments_inner<'tcx, T>( pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause() }); - 'diving_in_skip_visited_traits: loop { - if let Some(next_super_trait) = direct_super_traits_iter.next() { - if visited.insert(next_super_trait.to_predicate(tcx)) { - // We're throwing away potential constness of super traits here. - // FIXME: handle ~const super traits - let next_super_trait = next_super_trait.map_bound(|t| t.trait_ref); - stack.push(( - next_super_trait, - emit_vptr_on_new_entry, - Some(direct_super_traits_iter), - )); - break 'diving_in_skip_visited_traits; - } else { - continue 'diving_in_skip_visited_traits; - } - } else { - break 'diving_in; + // Find an unvisited supertrait + match direct_super_traits_iter + .find(|&super_trait| visited.insert(super_trait.to_predicate(tcx))) + { + // Push it to the stack for the next iteration of 'diving_in to pick up + Some(unvisited_super_trait) => { + // We're throwing away potential constness of super traits here. + // FIXME: handle ~const super traits + let next_super_trait = unvisited_super_trait.map_bound(|t| t.trait_ref); + stack.push(( + next_super_trait, + emit_vptr_on_new_entry, + Some(direct_super_traits_iter), + )) } + + // There are no more unvisited direct super traits, dive-in finished + None => break 'diving_in, } } From d567f0fc688411a8805c0187957b8bdcc84ef1d8 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 19 Jul 2023 10:19:12 +0000 Subject: [PATCH 025/113] Slightly refactor 'exiting_out loop in `prepare_vtable_segments` 1. Hide the option as an iterator, so it's nicer to work with 2. Replace a loop with `find` --- .../src/traits/vtable.rs | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 89b5272083ec..c00161d158ff 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -87,7 +87,7 @@ fn prepare_vtable_segments_inner<'tcx, T>( let mut visited = PredicateSet::new(tcx); let predicate = trait_ref.without_const().to_predicate(tcx); let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> = - smallvec![(trait_ref, emit_vptr_on_new_entry, None)]; + smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))]; visited.insert(predicate); // the main traversal loop: @@ -138,7 +138,7 @@ fn prepare_vtable_segments_inner<'tcx, T>( stack.push(( next_super_trait, emit_vptr_on_new_entry, - Some(direct_super_traits_iter), + maybe_iter(Some(direct_super_traits_iter)), )) } @@ -152,30 +152,26 @@ fn prepare_vtable_segments_inner<'tcx, T>( // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. 'exiting_out: loop { - if let Some((inner_most_trait_ref, emit_vptr, siblings_opt)) = stack.last_mut() { + if let Some((inner_most_trait_ref, emit_vptr, siblings)) = stack.last_mut() { segment_visitor(VtblSegment::TraitOwnEntries { trait_ref: *inner_most_trait_ref, emit_vptr: *emit_vptr, })?; - 'exiting_out_skip_visited_traits: loop { - if let Some(siblings) = siblings_opt { - if let Some(next_inner_most_trait_ref) = siblings.next() { - if visited.insert(next_inner_most_trait_ref.to_predicate(tcx)) { - // We're throwing away potential constness of super traits here. - // FIXME: handle ~const super traits - let next_inner_most_trait_ref = - next_inner_most_trait_ref.map_bound(|t| t.trait_ref); - *inner_most_trait_ref = next_inner_most_trait_ref; - *emit_vptr = emit_vptr_on_new_entry; - break 'exiting_out; - } else { - continue 'exiting_out_skip_visited_traits; - } - } + match siblings.find(|&sibling| visited.insert(sibling.to_predicate(tcx))) { + Some(next_inner_most_trait_ref) => { + // We're throwing away potential constness of super traits here. + // FIXME: handle ~const super traits + let next_inner_most_trait_ref = + next_inner_most_trait_ref.map_bound(|t| t.trait_ref); + *inner_most_trait_ref = next_inner_most_trait_ref; + *emit_vptr = emit_vptr_on_new_entry; + break 'exiting_out; + } + None => { + stack.pop(); + continue 'exiting_out; } - stack.pop(); - continue 'exiting_out; } } // all done @@ -184,6 +180,12 @@ fn prepare_vtable_segments_inner<'tcx, T>( } } +/// Turns option of iterator into an iterator (this is just flatten) +fn maybe_iter(i: Option) -> impl Iterator { + // Flatten is bad perf-vise, we could probably implement a special case here that is better + i.into_iter().flatten() +} + fn dump_vtable_entries<'tcx>( tcx: TyCtxt<'tcx>, sp: Span, From d87db8eb3f470d5cc8901dd74f6a25685511161a Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 19 Jul 2023 10:34:35 +0000 Subject: [PATCH 026/113] Simplify last `prepare_vtable_segments` loop even more --- .../src/traits/vtable.rs | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index c00161d158ff..3a647b784ccf 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -113,7 +113,7 @@ fn prepare_vtable_segments_inner<'tcx, T>( // Loop run #1: Emitting the slice [D C] (in reverse order). No one has a next-sibling node. // Loop run #1: Stack after exiting out is []. Now the function exits. - loop { + 'outer: loop { // dive deeper into the stack, recording the path 'diving_in: loop { let &(inner_most_trait_ref, _, _) = stack.last().unwrap(); @@ -151,32 +151,29 @@ fn prepare_vtable_segments_inner<'tcx, T>( emit_vptr_on_new_entry = true; // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. - 'exiting_out: loop { - if let Some((inner_most_trait_ref, emit_vptr, siblings)) = stack.last_mut() { - segment_visitor(VtblSegment::TraitOwnEntries { - trait_ref: *inner_most_trait_ref, - emit_vptr: *emit_vptr, - })?; + while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() { + segment_visitor(VtblSegment::TraitOwnEntries { + trait_ref: inner_most_trait_ref, + emit_vptr, + })?; - match siblings.find(|&sibling| visited.insert(sibling.to_predicate(tcx))) { - Some(next_inner_most_trait_ref) => { - // We're throwing away potential constness of super traits here. - // FIXME: handle ~const super traits - let next_inner_most_trait_ref = - next_inner_most_trait_ref.map_bound(|t| t.trait_ref); - *inner_most_trait_ref = next_inner_most_trait_ref; - *emit_vptr = emit_vptr_on_new_entry; - break 'exiting_out; - } - None => { - stack.pop(); - continue 'exiting_out; - } - } + if let Some(next_inner_most_trait_ref) = + siblings.find(|&sibling| visited.insert(sibling.to_predicate(tcx))) + { + // We're throwing away potential constness of super traits here. + // FIXME: handle ~const super traits + let next_inner_most_trait_ref = + next_inner_most_trait_ref.map_bound(|t| t.trait_ref); + + stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings)); + + // just pushed a new trait onto the stack, so we need to go through its super traits + continue 'outer; } - // all done - return ControlFlow::Continue(()); } + + // the stack is empty, all done + return ControlFlow::Continue(()); } } From f33936c567829d024a829f25c40b1e6dca9c75c6 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 19 Jul 2023 11:40:51 +0000 Subject: [PATCH 027/113] Fix comment --- compiler/rustc_trait_selection/src/traits/vtable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 3a647b784ccf..13ece4ccebb2 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -92,7 +92,7 @@ fn prepare_vtable_segments_inner<'tcx, T>( // the main traversal loop: // basically we want to cut the inheritance directed graph into a few non-overlapping slices of nodes - // that each node is emitted after all its descendents have been emitted. + // such that each node is emitted after all its descendants have been emitted. // so we convert the directed graph into a tree by skipping all previously visited nodes using a visited set. // this is done on the fly. // Each loop run emits a slice - it starts by find a "childless" unvisited node, backtracking upwards, and it From b5d122850821f80f9496abc09840102561ec4267 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 19 Jul 2023 11:45:04 +0000 Subject: [PATCH 028/113] Add a (failing test) for issue `113840` --- tests/ui/traits/vtable/multiple-markers.rs | 47 +++++++++++++++ .../ui/traits/vtable/multiple-markers.stderr | 58 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 tests/ui/traits/vtable/multiple-markers.rs create mode 100644 tests/ui/traits/vtable/multiple-markers.stderr diff --git a/tests/ui/traits/vtable/multiple-markers.rs b/tests/ui/traits/vtable/multiple-markers.rs new file mode 100644 index 000000000000..1e6e30870276 --- /dev/null +++ b/tests/ui/traits/vtable/multiple-markers.rs @@ -0,0 +1,47 @@ +// Regression test for +// +// This test makes sure that multiple marker (method-less) traits can reuse the +// same pointer for upcasting. +// +// build-fail +#![crate_type = "lib"] +#![feature(rustc_attrs)] + +// Markers +trait M0 {} +trait M1 {} +trait M2 {} + +// Just a trait with a method +trait T { + fn method(&self) {} +} + +#[rustc_dump_vtable] +trait A: M0 + M1 + M2 + T {} //~ error: vtable entries for ``: + +#[rustc_dump_vtable] +trait B: M0 + M1 + T + M2 {} //~ error: vtable entries for ``: + +#[rustc_dump_vtable] +trait C: M0 + T + M1 + M2 {} //~ error: vtable entries for ``: + +#[rustc_dump_vtable] +trait D: T + M0 + M1 + M2 {} //~ error: vtable entries for ``: + +struct S; + +impl M0 for S {} +impl M1 for S {} +impl M2 for S {} +impl T for S {} +impl A for S {} +impl B for S {} +impl C for S {} +impl D for S {} + +pub fn require_vtables() { + fn require_vtables(_: &dyn A, _: &dyn B, _: &dyn C, _: &dyn D) {} + + require_vtables(&S, &S, &S, &S) +} diff --git a/tests/ui/traits/vtable/multiple-markers.stderr b/tests/ui/traits/vtable/multiple-markers.stderr new file mode 100644 index 000000000000..766be12487b9 --- /dev/null +++ b/tests/ui/traits/vtable/multiple-markers.stderr @@ -0,0 +1,58 @@ +error: vtable entries for ``: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + TraitVPtr(), + TraitVPtr(), + Method(::method), + TraitVPtr(), + ] + --> $DIR/multiple-markers.rs:21:1 + | +LL | trait A: M0 + M1 + M2 + T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: vtable entries for ``: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + TraitVPtr(), + Method(::method), + TraitVPtr(), + TraitVPtr(), + ] + --> $DIR/multiple-markers.rs:24:1 + | +LL | trait B: M0 + M1 + T + M2 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: vtable entries for ``: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method(::method), + TraitVPtr(), + TraitVPtr(), + TraitVPtr(), + ] + --> $DIR/multiple-markers.rs:27:1 + | +LL | trait C: M0 + T + M1 + M2 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: vtable entries for ``: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method(::method), + TraitVPtr(), + TraitVPtr(), + TraitVPtr(), + ] + --> $DIR/multiple-markers.rs:30:1 + | +LL | trait D: T + M0 + M1 + M2 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + From 1f02c75718706d6180f9b25f55aa50f1b4ce75e1 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 19 Jul 2023 11:50:40 +0000 Subject: [PATCH 029/113] Don't emit useless vptrs for marker traits --- .../src/traits/vtable.rs | 25 ++++++++++++++++--- tests/ui/traits/object/print_vtable_sizes.rs | 5 ++-- .../traits/object/print_vtable_sizes.stdout | 2 +- .../ui/traits/vtable/multiple-markers.stderr | 6 ----- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 13ece4ccebb2..b4f614e3e220 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -147,9 +147,6 @@ fn prepare_vtable_segments_inner<'tcx, T>( } } - // Other than the left-most path, vptr should be emitted for each trait. - emit_vptr_on_new_entry = true; - // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() { segment_visitor(VtblSegment::TraitOwnEntries { @@ -157,6 +154,14 @@ fn prepare_vtable_segments_inner<'tcx, T>( emit_vptr, })?; + // If we've emitted (fed to `segment_visitor`) a trait that has methods present in the vtable, + // we'll need to emit vptrs from now on. + if !emit_vptr_on_new_entry + && has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id()) + { + emit_vptr_on_new_entry = true; + } + if let Some(next_inner_most_trait_ref) = siblings.find(|&sibling| visited.insert(sibling.to_predicate(tcx))) { @@ -196,11 +201,23 @@ fn dump_vtable_entries<'tcx>( }); } +fn has_own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { + own_existential_vtable_entries_iter(tcx, trait_def_id).next().is_some() +} + fn own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &[DefId] { + tcx.arena.alloc_from_iter(own_existential_vtable_entries_iter(tcx, trait_def_id)) +} + +fn own_existential_vtable_entries_iter( + tcx: TyCtxt<'_>, + trait_def_id: DefId, +) -> impl Iterator + '_ { let trait_methods = tcx .associated_items(trait_def_id) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Fn); + // Now list each method's DefId (for within its trait). let own_entries = trait_methods.filter_map(move |&trait_method| { debug!("own_existential_vtable_entry: trait_method={:?}", trait_method); @@ -215,7 +232,7 @@ fn own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &[Def Some(def_id) }); - tcx.arena.alloc_from_iter(own_entries.into_iter()) + own_entries } /// Given a trait `trait_ref`, iterates the vtable entries diff --git a/tests/ui/traits/object/print_vtable_sizes.rs b/tests/ui/traits/object/print_vtable_sizes.rs index 5656094990be..f510608537ab 100644 --- a/tests/ui/traits/object/print_vtable_sizes.rs +++ b/tests/ui/traits/object/print_vtable_sizes.rs @@ -10,8 +10,9 @@ trait C { fn x() {} // not object safe, shouldn't be reported } -// This ideally should not have any upcasting cost, -// but currently does due to a bug +// This does not have any upcasting cost, +// because both `Send` and `Sync` are traits +// with no methods trait D: Send + Sync + help::MarkerWithSuper {} // This can't have no cost without reordering, diff --git a/tests/ui/traits/object/print_vtable_sizes.stdout b/tests/ui/traits/object/print_vtable_sizes.stdout index 3ba650bc3604..ce90c76217b9 100644 --- a/tests/ui/traits/object/print_vtable_sizes.stdout +++ b/tests/ui/traits/object/print_vtable_sizes.stdout @@ -1,8 +1,8 @@ -print-vtable-sizes { "crate_name": "", "trait_name": "D", "entries": "7", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "3", "upcasting_cost_percent": "75" } print-vtable-sizes { "crate_name": "", "trait_name": "E", "entries": "6", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "2", "upcasting_cost_percent": "50" } print-vtable-sizes { "crate_name": "", "trait_name": "G", "entries": "14", "entries_ignoring_upcasting": "11", "entries_for_upcasting": "3", "upcasting_cost_percent": "27.27272727272727" } print-vtable-sizes { "crate_name": "", "trait_name": "A", "entries": "6", "entries_ignoring_upcasting": "5", "entries_for_upcasting": "1", "upcasting_cost_percent": "20" } print-vtable-sizes { "crate_name": "", "trait_name": "B", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } +print-vtable-sizes { "crate_name": "", "trait_name": "D", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } print-vtable-sizes { "crate_name": "", "trait_name": "F", "entries": "6", "entries_ignoring_upcasting": "6", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } print-vtable-sizes { "crate_name": "", "trait_name": "_::S", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } print-vtable-sizes { "crate_name": "", "trait_name": "_::S", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } diff --git a/tests/ui/traits/vtable/multiple-markers.stderr b/tests/ui/traits/vtable/multiple-markers.stderr index 766be12487b9..4497c703ae8a 100644 --- a/tests/ui/traits/vtable/multiple-markers.stderr +++ b/tests/ui/traits/vtable/multiple-markers.stderr @@ -2,10 +2,7 @@ error: vtable entries for ``: [ MetadataDropInPlace, MetadataSize, MetadataAlign, - TraitVPtr(), - TraitVPtr(), Method(::method), - TraitVPtr(), ] --> $DIR/multiple-markers.rs:21:1 | @@ -16,9 +13,7 @@ error: vtable entries for ``: [ MetadataDropInPlace, MetadataSize, MetadataAlign, - TraitVPtr(), Method(::method), - TraitVPtr(), TraitVPtr(), ] --> $DIR/multiple-markers.rs:24:1 @@ -31,7 +26,6 @@ error: vtable entries for ``: [ MetadataSize, MetadataAlign, Method(::method), - TraitVPtr(), TraitVPtr(), TraitVPtr(), ] From 45ffe41d144e3e5bc8993abcbcdd54d870a2ff53 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 17 Jul 2023 18:55:07 +0000 Subject: [PATCH 030/113] Substitute types before checking compatibility. --- compiler/rustc_mir_transform/src/inline.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 7860cf762479..e08edfe143a4 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -440,6 +440,10 @@ impl<'tcx> Inliner<'tcx> { validation: Ok(()), }; + for var_debug_info in callee_body.var_debug_info.iter() { + checker.visit_var_debug_info(var_debug_info); + } + // Traverse the MIR manually so we can account for the effects of inlining on the CFG. let mut work_list = vec![START_BLOCK]; let mut visited = BitSet::new_empty(callee_body.basic_blocks.len()); @@ -847,7 +851,16 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { if let ProjectionElem::Field(f, ty) = elem { let parent_ty = place_ref.ty(&self.callee_body.local_decls, self.tcx); let check_equal = |this: &mut Self, f_ty| { - if !util::is_equal_up_to_subtyping(this.tcx, this.param_env, ty, f_ty) { + // Fast path if there is nothing to substitute. + if ty == f_ty { + return; + } + let ty = this.instance.subst_mir(this.tcx, ty::EarlyBinder::bind(&ty)); + let f_ty = this.instance.subst_mir(this.tcx, ty::EarlyBinder::bind(&f_ty)); + if ty == f_ty { + return; + } + if !util::is_subtype(this.tcx, this.param_env, ty, f_ty) { trace!(?ty, ?f_ty); this.validation = Err("failed to normalize projection type"); return; From 68077d58272901590fb2b1de5dc551a88a1dc36c Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 18 Jul 2023 12:11:49 -0300 Subject: [PATCH 031/113] Rename SMIR AdtSubsts to GenericArgs --- compiler/rustc_smir/src/rustc_smir/mod.rs | 8 ++++---- compiler/rustc_smir/src/stable_mir/ty.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 922bbf029387..f624c4dc8ff4 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -8,7 +8,7 @@ //! For now, we are developing everything inside `rustc`, thus, we keep this module private. use crate::rustc_internal::{self, opaque}; -use crate::stable_mir::ty::{AdtSubsts, FloatTy, GenericArgKind, IntTy, RigidTy, TyKind, UintTy}; +use crate::stable_mir::ty::{FloatTy, GenericArgs, GenericArgKind, IntTy, RigidTy, TyKind, UintTy}; use crate::stable_mir::{self, Context}; use rustc_middle::mir; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -94,10 +94,10 @@ impl<'tcx> Tables<'tcx> { ty::FloatTy::F32 => TyKind::RigidTy(RigidTy::Float(FloatTy::F32)), ty::FloatTy::F64 => TyKind::RigidTy(RigidTy::Float(FloatTy::F64)), }, - ty::Adt(adt_def, substs) => TyKind::RigidTy(RigidTy::Adt( + ty::Adt(adt_def, generic_args) => TyKind::RigidTy(RigidTy::Adt( rustc_internal::adt_def(adt_def.did()), - AdtSubsts( - substs + GenericArgs( + generic_args .iter() .map(|arg| match arg.unpack() { ty::GenericArgKind::Lifetime(region) => { diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 571fd0d74724..36b562850e95 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -25,7 +25,7 @@ pub enum RigidTy { Int(IntTy), Uint(UintTy), Float(FloatTy), - Adt(AdtDef, AdtSubsts), + Adt(AdtDef, GenericArgs), Foreign(ForeignDef), Str, Array(Ty, Const), @@ -69,7 +69,7 @@ pub struct ForeignDef(pub(crate) DefId); pub struct AdtDef(pub(crate) DefId); #[derive(Clone, Debug)] -pub struct AdtSubsts(pub Vec); +pub struct GenericArgs(pub Vec); #[derive(Clone, Debug)] pub enum GenericArgKind { From e5c0b96e2408850d6db27a8d69339fa6a6c78455 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 18 Jul 2023 12:16:38 -0300 Subject: [PATCH 032/113] Add FnDef ty to SMIR --- compiler/rustc_smir/src/rustc_internal/mod.rs | 8 +++++++ compiler/rustc_smir/src/rustc_smir/mod.rs | 22 +++++++++++++++++-- compiler/rustc_smir/src/stable_mir/ty.rs | 4 ++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 2ac3046ce6d7..e4f129bc1d6c 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -35,6 +35,10 @@ pub fn foreign_def(did: DefId) -> stable_mir::ty::ForeignDef { with_tables(|t| t.foreign_def(did)) } +pub fn fn_def(did: DefId) -> stable_mir::ty::FnDef { + with_tables(|t| t.fn_def(did)) +} + impl<'tcx> Tables<'tcx> { pub fn item_def_id(&self, item: &stable_mir::CrateItem) -> DefId { self.def_ids[item.0] @@ -52,6 +56,10 @@ impl<'tcx> Tables<'tcx> { stable_mir::ty::ForeignDef(self.create_def_id(did)) } + pub fn fn_def(&mut self, did: DefId) -> stable_mir::ty::FnDef { + stable_mir::ty::FnDef(self.create_def_id(did)) + } + fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId { // FIXME: this becomes inefficient when we have too many ids for (i, &d) in self.def_ids.iter().enumerate() { diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index f624c4dc8ff4..81ca0f985cc7 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -8,7 +8,7 @@ //! For now, we are developing everything inside `rustc`, thus, we keep this module private. use crate::rustc_internal::{self, opaque}; -use crate::stable_mir::ty::{FloatTy, GenericArgs, GenericArgKind, IntTy, RigidTy, TyKind, UintTy}; +use crate::stable_mir::ty::{FloatTy, GenericArgKind, GenericArgs, IntTy, RigidTy, TyKind, UintTy}; use crate::stable_mir::{self, Context}; use rustc_middle::mir; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -127,7 +127,25 @@ impl<'tcx> Tables<'tcx> { ty::Ref(region, ty, mutbl) => { TyKind::RigidTy(RigidTy::Ref(opaque(region), self.intern_ty(*ty), mutbl.stable())) } - ty::FnDef(_, _) => todo!(), + ty::FnDef(def_id, generic_args) => TyKind::RigidTy(RigidTy::FnDef( + rustc_internal::fn_def(*def_id), + GenericArgs( + generic_args + .iter() + .map(|arg| match arg.unpack() { + ty::GenericArgKind::Lifetime(region) => { + GenericArgKind::Lifetime(opaque(®ion)) + } + ty::GenericArgKind::Type(ty) => { + GenericArgKind::Type(self.intern_ty(ty)) + } + ty::GenericArgKind::Const(const_) => { + GenericArgKind::Const(opaque(&const_)) + } + }) + .collect(), + ), + )), ty::FnPtr(_) => todo!(), ty::Dynamic(_, _, _) => todo!(), ty::Closure(_, _) => todo!(), diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 36b562850e95..f3dbde907a03 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -32,6 +32,7 @@ pub enum RigidTy { Slice(Ty), RawPtr(Ty, Mutability), Ref(Region, Ty, Mutability), + FnDef(FnDef, GenericArgs), Never, Tuple(Vec), } @@ -65,6 +66,9 @@ pub enum FloatTy { #[derive(Clone, PartialEq, Eq, Debug)] pub struct ForeignDef(pub(crate) DefId); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FnDef(pub(crate) DefId); + #[derive(Clone, PartialEq, Eq, Debug)] pub struct AdtDef(pub(crate) DefId); From c5c38cdee80e42c374a0afddeb843851abfd105b Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 18 Jul 2023 12:19:41 -0300 Subject: [PATCH 033/113] Add Closure ty to SMIR --- compiler/rustc_smir/src/rustc_internal/mod.rs | 8 ++++++++ compiler/rustc_smir/src/rustc_smir/mod.rs | 20 ++++++++++++++++++- compiler/rustc_smir/src/stable_mir/ty.rs | 4 ++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index e4f129bc1d6c..d32215186a63 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -39,6 +39,10 @@ pub fn fn_def(did: DefId) -> stable_mir::ty::FnDef { with_tables(|t| t.fn_def(did)) } +pub fn closure_def(did: DefId) -> stable_mir::ty::ClosureDef { + with_tables(|t| t.closure_def(did)) +} + impl<'tcx> Tables<'tcx> { pub fn item_def_id(&self, item: &stable_mir::CrateItem) -> DefId { self.def_ids[item.0] @@ -60,6 +64,10 @@ impl<'tcx> Tables<'tcx> { stable_mir::ty::FnDef(self.create_def_id(did)) } + pub fn closure_def(&mut self, did: DefId) -> stable_mir::ty::ClosureDef { + stable_mir::ty::ClosureDef(self.create_def_id(did)) + } + fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId { // FIXME: this becomes inefficient when we have too many ids for (i, &d) in self.def_ids.iter().enumerate() { diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 81ca0f985cc7..b542cc84f6a7 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -148,7 +148,25 @@ impl<'tcx> Tables<'tcx> { )), ty::FnPtr(_) => todo!(), ty::Dynamic(_, _, _) => todo!(), - ty::Closure(_, _) => todo!(), + ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure( + rustc_internal::closure_def(*def_id), + GenericArgs( + generic_args + .iter() + .map(|arg| match arg.unpack() { + ty::GenericArgKind::Lifetime(region) => { + GenericArgKind::Lifetime(opaque(®ion)) + } + ty::GenericArgKind::Type(ty) => { + GenericArgKind::Type(self.intern_ty(ty)) + } + ty::GenericArgKind::Const(const_) => { + GenericArgKind::Const(opaque(&const_)) + } + }) + .collect(), + ), + )), ty::Generator(_, _, _) => todo!(), ty::Never => TyKind::RigidTy(RigidTy::Never), ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple( diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index f3dbde907a03..758e0bb7de70 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -33,6 +33,7 @@ pub enum RigidTy { RawPtr(Ty, Mutability), Ref(Region, Ty, Mutability), FnDef(FnDef, GenericArgs), + Closure(ClosureDef, GenericArgs), Never, Tuple(Vec), } @@ -69,6 +70,9 @@ pub struct ForeignDef(pub(crate) DefId); #[derive(Clone, PartialEq, Eq, Debug)] pub struct FnDef(pub(crate) DefId); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct ClosureDef(pub(crate) DefId); + #[derive(Clone, PartialEq, Eq, Debug)] pub struct AdtDef(pub(crate) DefId); From ed323476891eb948d6c6e6390756f0052c765453 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 18 Jul 2023 13:38:16 -0300 Subject: [PATCH 034/113] Add Generator to SMIR --- compiler/rustc_smir/src/rustc_internal/mod.rs | 8 ++++++ compiler/rustc_smir/src/rustc_smir/mod.rs | 28 +++++++++++++++++-- compiler/rustc_smir/src/stable_mir/ty.rs | 10 +++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index d32215186a63..ccb12c27107e 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -43,6 +43,10 @@ pub fn closure_def(did: DefId) -> stable_mir::ty::ClosureDef { with_tables(|t| t.closure_def(did)) } +pub fn generator_def(did: DefId) -> stable_mir::ty::GeneratorDef { + with_tables(|t| t.generator_def(did)) +} + impl<'tcx> Tables<'tcx> { pub fn item_def_id(&self, item: &stable_mir::CrateItem) -> DefId { self.def_ids[item.0] @@ -68,6 +72,10 @@ impl<'tcx> Tables<'tcx> { stable_mir::ty::ClosureDef(self.create_def_id(did)) } + pub fn generator_def(&mut self, did: DefId) -> stable_mir::ty::GeneratorDef { + stable_mir::ty::GeneratorDef(self.create_def_id(did)) + } + fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId { // FIXME: this becomes inefficient when we have too many ids for (i, &d) in self.def_ids.iter().enumerate() { diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index b542cc84f6a7..8bc7ef325cff 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -8,8 +8,9 @@ //! For now, we are developing everything inside `rustc`, thus, we keep this module private. use crate::rustc_internal::{self, opaque}; -use crate::stable_mir::ty::{FloatTy, GenericArgKind, GenericArgs, IntTy, RigidTy, TyKind, UintTy}; +use crate::stable_mir::ty::{FloatTy, GenericArgKind, GenericArgs, IntTy, Movability, RigidTy, TyKind, UintTy}; use crate::stable_mir::{self, Context}; +use rustc_hir as hir; use rustc_middle::mir; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE}; @@ -167,7 +168,30 @@ impl<'tcx> Tables<'tcx> { .collect(), ), )), - ty::Generator(_, _, _) => todo!(), + ty::Generator(def_id, generic_args, movability) => TyKind::RigidTy(RigidTy::Generator( + rustc_internal::generator_def(*def_id), + GenericArgs( + generic_args + .iter() + .map(|arg| match arg.unpack() { + ty::GenericArgKind::Lifetime(region) => { + GenericArgKind::Lifetime(opaque(®ion)) + } + ty::GenericArgKind::Type(ty) => { + GenericArgKind::Type(self.intern_ty(ty)) + } + ty::GenericArgKind::Const(const_) => { + GenericArgKind::Const(opaque(&const_)) + } + }) + .collect(), + ), + match movability { + hir::Movability::Static => Movability::Static, + hir::Movability::Movable => Movability::Movable, + + } + )), ty::Never => TyKind::RigidTy(RigidTy::Never), ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple( fields.iter().map(|ty| self.intern_ty(ty)).collect(), diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 758e0bb7de70..1ccb30343ed8 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -34,6 +34,7 @@ pub enum RigidTy { Ref(Region, Ty, Mutability), FnDef(FnDef, GenericArgs), Closure(ClosureDef, GenericArgs), + Generator(GeneratorDef, GenericArgs, Movability), Never, Tuple(Vec), } @@ -64,6 +65,12 @@ pub enum FloatTy { F64, } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Movability { + Static, + Movable, +} + #[derive(Clone, PartialEq, Eq, Debug)] pub struct ForeignDef(pub(crate) DefId); @@ -73,6 +80,9 @@ pub struct FnDef(pub(crate) DefId); #[derive(Clone, PartialEq, Eq, Debug)] pub struct ClosureDef(pub(crate) DefId); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct GeneratorDef(pub(crate) DefId); + #[derive(Clone, PartialEq, Eq, Debug)] pub struct AdtDef(pub(crate) DefId); From db35f1de2f7e4a23e6401ee7e2a3335c29d33519 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 19 Jul 2023 11:01:58 -0300 Subject: [PATCH 035/113] Extract generic_args function --- compiler/rustc_smir/src/rustc_smir/mod.rs | 93 +++++++---------------- 1 file changed, 26 insertions(+), 67 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 8bc7ef325cff..f512a98f41a4 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -8,7 +8,9 @@ //! For now, we are developing everything inside `rustc`, thus, we keep this module private. use crate::rustc_internal::{self, opaque}; -use crate::stable_mir::ty::{FloatTy, GenericArgKind, GenericArgs, IntTy, Movability, RigidTy, TyKind, UintTy}; +use crate::stable_mir::ty::{ + FloatTy, GenericArgKind, GenericArgs, IntTy, Movability, RigidTy, TyKind, UintTy, +}; use crate::stable_mir::{self, Context}; use rustc_hir as hir; use rustc_middle::mir; @@ -97,22 +99,7 @@ impl<'tcx> Tables<'tcx> { }, ty::Adt(adt_def, generic_args) => TyKind::RigidTy(RigidTy::Adt( rustc_internal::adt_def(adt_def.did()), - GenericArgs( - generic_args - .iter() - .map(|arg| match arg.unpack() { - ty::GenericArgKind::Lifetime(region) => { - GenericArgKind::Lifetime(opaque(®ion)) - } - ty::GenericArgKind::Type(ty) => { - GenericArgKind::Type(self.intern_ty(ty)) - } - ty::GenericArgKind::Const(const_) => { - GenericArgKind::Const(opaque(&const_)) - } - }) - .collect(), - ), + self.generic_args(generic_args), )), ty::Foreign(def_id) => { TyKind::RigidTy(RigidTy::Foreign(rustc_internal::foreign_def(*def_id))) @@ -130,67 +117,21 @@ impl<'tcx> Tables<'tcx> { } ty::FnDef(def_id, generic_args) => TyKind::RigidTy(RigidTy::FnDef( rustc_internal::fn_def(*def_id), - GenericArgs( - generic_args - .iter() - .map(|arg| match arg.unpack() { - ty::GenericArgKind::Lifetime(region) => { - GenericArgKind::Lifetime(opaque(®ion)) - } - ty::GenericArgKind::Type(ty) => { - GenericArgKind::Type(self.intern_ty(ty)) - } - ty::GenericArgKind::Const(const_) => { - GenericArgKind::Const(opaque(&const_)) - } - }) - .collect(), - ), + self.generic_args(generic_args), )), ty::FnPtr(_) => todo!(), ty::Dynamic(_, _, _) => todo!(), ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure( rustc_internal::closure_def(*def_id), - GenericArgs( - generic_args - .iter() - .map(|arg| match arg.unpack() { - ty::GenericArgKind::Lifetime(region) => { - GenericArgKind::Lifetime(opaque(®ion)) - } - ty::GenericArgKind::Type(ty) => { - GenericArgKind::Type(self.intern_ty(ty)) - } - ty::GenericArgKind::Const(const_) => { - GenericArgKind::Const(opaque(&const_)) - } - }) - .collect(), - ), + self.generic_args(generic_args), )), ty::Generator(def_id, generic_args, movability) => TyKind::RigidTy(RigidTy::Generator( rustc_internal::generator_def(*def_id), - GenericArgs( - generic_args - .iter() - .map(|arg| match arg.unpack() { - ty::GenericArgKind::Lifetime(region) => { - GenericArgKind::Lifetime(opaque(®ion)) - } - ty::GenericArgKind::Type(ty) => { - GenericArgKind::Type(self.intern_ty(ty)) - } - ty::GenericArgKind::Const(const_) => { - GenericArgKind::Const(opaque(&const_)) - } - }) - .collect(), - ), + self.generic_args(generic_args), match movability { hir::Movability::Static => Movability::Static, hir::Movability::Movable => Movability::Movable, - - } + }, )), ty::Never => TyKind::RigidTy(RigidTy::Never), ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple( @@ -217,6 +158,24 @@ impl<'tcx> Tables<'tcx> { self.types.push(ty); stable_mir::ty::Ty(id) } + + fn generic_args( + &mut self, + generic_args: &ty::GenericArgs<'tcx>, + ) -> stable_mir::ty::GenericArgs { + GenericArgs( + generic_args + .iter() + .map(|arg| match arg.unpack() { + ty::GenericArgKind::Lifetime(region) => { + GenericArgKind::Lifetime(opaque(®ion)) + } + ty::GenericArgKind::Type(ty) => GenericArgKind::Type(self.intern_ty(ty)), + ty::GenericArgKind::Const(const_) => GenericArgKind::Const(opaque(&const_)), + }) + .collect(), + ) + } } /// Build a stable mir crate from a given crate number. From c5819b2b9b51a6be498c11ceadae00ffbed8946d Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 19 Jul 2023 11:03:39 -0300 Subject: [PATCH 036/113] Remove FIXMEs a lot of things need fixes --- compiler/rustc_smir/src/stable_mir/ty.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 1ccb30343ed8..ba120be04b2f 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -91,9 +91,7 @@ pub struct GenericArgs(pub Vec); #[derive(Clone, Debug)] pub enum GenericArgKind { - // FIXME add proper region Lifetime(Region), Type(Ty), - // FIXME add proper const Const(Const), } From 008be2d7b6cc974e66e9e145854965cb508f6b3c Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 14 Jul 2023 14:50:21 +0000 Subject: [PATCH 037/113] Verify that all crate sources are in sync This ensures that rustc will not attempt to link against a cdylib as if it is a rust dylib when an rlib for the same crate is available. Previously rustc didn't actually check if any further formats of a crate which has been loaded are of the same version and if they are actually valid. This caused a cdylib to be interpreted as rust dylib as soon as the corresponding rlib was loaded. As cdylibs don't export any rust symbols, linking would fail if rustc decides to link against the cdylib rather than the rlib. Two crates depended on the previous behavior by separately compiling a test crate as both rlib and dylib. These have been changed to capture their original spirit to the best of my ability while still working when rustc verifies that all crates are in sync. It is unlikely that build systems depend on the current behavior and in any case we are taking a lot of measures to ensure that any change to either the source or the compilation options (including crate type) results in rustc rejecting it as incompatible. We merely didn't do this check here for now obsolete perf reasons. --- compiler/rustc_metadata/src/locator.rs | 18 ++++--------- tests/run-make/extern-flag-pathless/Makefile | 27 ++++++++++++++----- .../extern-flag-pathless/bar-dynamic.rs | 3 --- .../extern-flag-pathless/bar-static.rs | 3 --- tests/run-make/extern-flag-pathless/bar.rs | 1 + tests/run-make/mixing-libs/Makefile | 8 +++--- tests/run-make/no-cdylib-as-rdylib/Makefile | 16 +++++++++++ tests/run-make/no-cdylib-as-rdylib/bar.rs | 1 + tests/run-make/no-cdylib-as-rdylib/foo.rs | 5 ++++ 9 files changed, 52 insertions(+), 30 deletions(-) delete mode 100644 tests/run-make/extern-flag-pathless/bar-dynamic.rs delete mode 100644 tests/run-make/extern-flag-pathless/bar-static.rs create mode 100644 tests/run-make/extern-flag-pathless/bar.rs create mode 100644 tests/run-make/no-cdylib-as-rdylib/Makefile create mode 100644 tests/run-make/no-cdylib-as-rdylib/bar.rs create mode 100644 tests/run-make/no-cdylib-as-rdylib/foo.rs diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index a1511c4b5704..ed72064ef806 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -511,7 +511,7 @@ impl<'a> CrateLocator<'a> { rlib: self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot)?, dylib: self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot)?, }; - Ok(slot.map(|(svh, metadata)| (svh, Library { source, metadata }))) + Ok(slot.map(|(_, svh, metadata)| (svh, Library { source, metadata }))) } fn needs_crate_flavor(&self, flavor: CrateFlavor) -> bool { @@ -539,7 +539,7 @@ impl<'a> CrateLocator<'a> { &mut self, m: FxHashMap, flavor: CrateFlavor, - slot: &mut Option<(Svh, MetadataBlob)>, + slot: &mut Option<(PathBuf, Svh, MetadataBlob)>, ) -> Result, CrateError> { // If we are producing an rlib, and we've already loaded metadata, then // we should not attempt to discover further crate sources (unless we're @@ -550,16 +550,9 @@ impl<'a> CrateLocator<'a> { // // See also #68149 which provides more detail on why emitting the // dependency on the rlib is a bad thing. - // - // We currently do not verify that these other sources are even in sync, - // and this is arguably a bug (see #10786), but because reading metadata - // is quite slow (especially from dylibs) we currently do not read it - // from the other crate sources. if slot.is_some() { if m.is_empty() || !self.needs_crate_flavor(flavor) { return Ok(None); - } else if m.len() == 1 { - return Ok(Some(m.into_iter().next().unwrap())); } } @@ -602,7 +595,7 @@ impl<'a> CrateLocator<'a> { } }; // If we see multiple hashes, emit an error about duplicate candidates. - if slot.as_ref().is_some_and(|s| s.0 != hash) { + if slot.as_ref().is_some_and(|s| s.1 != hash) { if let Some(candidates) = err_data { return Err(CrateError::MultipleCandidates( self.crate_name, @@ -610,8 +603,7 @@ impl<'a> CrateLocator<'a> { candidates, )); } - err_data = Some(vec![ret.as_ref().unwrap().0.clone()]); - *slot = None; + err_data = Some(vec![slot.take().unwrap().0]); } if let Some(candidates) = &mut err_data { candidates.push(lib); @@ -644,7 +636,7 @@ impl<'a> CrateLocator<'a> { continue; } } - *slot = Some((hash, metadata)); + *slot = Some((lib.clone(), hash, metadata)); ret = Some((lib, kind)); } diff --git a/tests/run-make/extern-flag-pathless/Makefile b/tests/run-make/extern-flag-pathless/Makefile index 701bfcd28c8a..36b374e0d2e1 100644 --- a/tests/run-make/extern-flag-pathless/Makefile +++ b/tests/run-make/extern-flag-pathless/Makefile @@ -3,17 +3,32 @@ include ../tools.mk # Test mixing pathless --extern with paths. +# Test for static linking by checking that the binary runs if the dylib +# is removed and test for dynamic linking by checking that the binary +# fails to run if the dylib is removed. + all: - $(RUSTC) bar-static.rs --crate-name=bar --crate-type=rlib - $(RUSTC) bar-dynamic.rs --crate-name=bar --crate-type=dylib -C prefer-dynamic + $(RUSTC) bar.rs --crate-type=rlib --crate-type=dylib -Cprefer-dynamic + # rlib preferred over dylib $(RUSTC) foo.rs --extern bar - $(call RUN,foo) | $(CGREP) 'static' + mv $(call DYLIB,bar) $(TMPDIR)/bar.tmp + $(call RUN,foo) + mv $(TMPDIR)/bar.tmp $(call DYLIB,bar) + $(RUSTC) foo.rs --extern bar=$(TMPDIR)/libbar.rlib --extern bar - $(call RUN,foo) | $(CGREP) 'static' + mv $(call DYLIB,bar) $(TMPDIR)/bar.tmp + $(call RUN,foo) + mv $(TMPDIR)/bar.tmp $(call DYLIB,bar) + # explicit --extern overrides pathless $(RUSTC) foo.rs --extern bar=$(call DYLIB,bar) --extern bar - $(call RUN,foo) | $(CGREP) 'dynamic' + mv $(call DYLIB,bar) $(TMPDIR)/bar.tmp + $(call FAIL,foo) + mv $(TMPDIR)/bar.tmp $(call DYLIB,bar) + # prefer-dynamic does what it says $(RUSTC) foo.rs --extern bar -C prefer-dynamic - $(call RUN,foo) | $(CGREP) 'dynamic' + mv $(call DYLIB,bar) $(TMPDIR)/bar.tmp + $(call FAIL,foo) + mv $(TMPDIR)/bar.tmp $(call DYLIB,bar) diff --git a/tests/run-make/extern-flag-pathless/bar-dynamic.rs b/tests/run-make/extern-flag-pathless/bar-dynamic.rs deleted file mode 100644 index e2d68d517ff9..000000000000 --- a/tests/run-make/extern-flag-pathless/bar-dynamic.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn f() { - println!("dynamic"); -} diff --git a/tests/run-make/extern-flag-pathless/bar-static.rs b/tests/run-make/extern-flag-pathless/bar-static.rs deleted file mode 100644 index 240d8bde4d18..000000000000 --- a/tests/run-make/extern-flag-pathless/bar-static.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn f() { - println!("static"); -} diff --git a/tests/run-make/extern-flag-pathless/bar.rs b/tests/run-make/extern-flag-pathless/bar.rs new file mode 100644 index 000000000000..cdc6c27d800b --- /dev/null +++ b/tests/run-make/extern-flag-pathless/bar.rs @@ -0,0 +1 @@ +pub fn f() {} diff --git a/tests/run-make/mixing-libs/Makefile b/tests/run-make/mixing-libs/Makefile index e8262b284011..459db0dfdb2f 100644 --- a/tests/run-make/mixing-libs/Makefile +++ b/tests/run-make/mixing-libs/Makefile @@ -2,9 +2,7 @@ include ../tools.mk all: - $(RUSTC) rlib.rs - $(RUSTC) dylib.rs - $(RUSTC) rlib.rs --crate-type=dylib - $(RUSTC) dylib.rs - $(call REMOVE_DYLIBS,rlib) + $(RUSTC) rlib.rs --crate-type=rlib --crate-type=dylib + $(RUSTC) dylib.rs # no -Cprefer-dynamic so statically linking librlib.rlib + $(call REMOVE_DYLIBS,rlib) # remove librlib.so to test that prog.rs doesn't get confused about the removed dylib version of librlib $(RUSTC) prog.rs && exit 1 || exit 0 diff --git a/tests/run-make/no-cdylib-as-rdylib/Makefile b/tests/run-make/no-cdylib-as-rdylib/Makefile new file mode 100644 index 000000000000..4d2be0aea913 --- /dev/null +++ b/tests/run-make/no-cdylib-as-rdylib/Makefile @@ -0,0 +1,16 @@ +# ignore-cross-compile +include ../tools.mk + +# Test that rustc will not attempt to link against a cdylib as if +# it is a rust dylib when an rlib for the same crate is available. +# Previously rustc didn't actually check if any further formats of +# a crate which has been loaded are of the same version and if +# they are actually valid. This caused a cdylib to be interpreted +# as rust dylib as soon as the corresponding rlib was loaded. As +# cdylibs don't export any rust symbols, linking would fail if +# rustc decides to link against the cdylib rather than the rlib. + +all: + $(RUSTC) bar.rs --crate-type=rlib --crate-type=cdylib + $(RUSTC) foo.rs -C prefer-dynamic + $(call RUN,foo) diff --git a/tests/run-make/no-cdylib-as-rdylib/bar.rs b/tests/run-make/no-cdylib-as-rdylib/bar.rs new file mode 100644 index 000000000000..c5c0bc606cd6 --- /dev/null +++ b/tests/run-make/no-cdylib-as-rdylib/bar.rs @@ -0,0 +1 @@ +pub fn bar() {} diff --git a/tests/run-make/no-cdylib-as-rdylib/foo.rs b/tests/run-make/no-cdylib-as-rdylib/foo.rs new file mode 100644 index 000000000000..8d68535e3b64 --- /dev/null +++ b/tests/run-make/no-cdylib-as-rdylib/foo.rs @@ -0,0 +1,5 @@ +extern crate bar; + +fn main() { + bar::bar(); +} From 52853c2694309353353a4ecba1a09a87791a7fd6 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 15 Jul 2023 10:03:48 +0000 Subject: [PATCH 038/113] Don't compress dylib metadata --- Cargo.lock | 1 - compiler/rustc_codegen_ssa/Cargo.toml | 1 - .../rustc_codegen_ssa/src/back/metadata.rs | 10 ++---- compiler/rustc_metadata/src/locator.rs | 31 ++++++++++++------- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb619d3870f3..0d839aba7316 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3367,7 +3367,6 @@ dependencies = [ "rustc_type_ir", "serde_json", "smallvec", - "snap", "tempfile", "thorin-dwp", "tracing", diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 984efa210444..6582fd62387c 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -17,7 +17,6 @@ tempfile = "3.2" thorin-dwp = "0.6" pathdiff = "0.2.0" serde_json = "1.0.59" -snap = "1" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } regex = "1.4" diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index e8b8665e39d4..9313b011cff6 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -10,8 +10,6 @@ use object::{ ObjectSymbol, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope, }; -use snap::write::FrameEncoder; - use rustc_data_structures::memmap::Mmap; use rustc_data_structures::owned_slice::{try_slice_owned, OwnedSlice}; use rustc_metadata::fs::METADATA_FILENAME; @@ -482,12 +480,8 @@ pub fn create_compressed_metadata_file( symbol_name: &str, ) -> Vec { let mut compressed = rustc_metadata::METADATA_HEADER.to_vec(); - // Our length will be backfilled once we're done writing - compressed.write_all(&[0; 4]).unwrap(); - FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap(); - let meta_len = rustc_metadata::METADATA_HEADER.len(); - let data_len = (compressed.len() - meta_len - 4) as u32; - compressed[meta_len..meta_len + 4].copy_from_slice(&data_len.to_be_bytes()); + compressed.write_all(&(metadata.raw_data().len() as u32).to_be_bytes()).unwrap(); + compressed.extend(metadata.raw_data()); let Some(mut file) = create_object_file(sess) else { return compressed.to_vec(); diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index ed72064ef806..30b7d33263ce 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -806,19 +806,26 @@ fn get_metadata_section<'p>( let compressed_len = u32::from_be_bytes(len_bytes) as usize; // Header is okay -> inflate the actual metadata - let compressed_bytes = &buf[data_start..(data_start + compressed_len)]; - debug!("inflating {} bytes of compressed metadata", compressed_bytes.len()); - // Assume the decompressed data will be at least the size of the compressed data, so we - // don't have to grow the buffer as much. - let mut inflated = Vec::with_capacity(compressed_bytes.len()); - FrameDecoder::new(compressed_bytes).read_to_end(&mut inflated).map_err(|_| { - MetadataError::LoadFailure(format!( - "failed to decompress metadata: {}", - filename.display() - )) - })?; + let compressed_bytes = buf.slice(|buf| &buf[data_start..(data_start + compressed_len)]); + if &compressed_bytes[..cmp::min(METADATA_HEADER.len(), compressed_bytes.len())] + == METADATA_HEADER + { + // The metadata was not actually compressed. + compressed_bytes + } else { + debug!("inflating {} bytes of compressed metadata", compressed_bytes.len()); + // Assume the decompressed data will be at least the size of the compressed data, so we + // don't have to grow the buffer as much. + let mut inflated = Vec::with_capacity(compressed_bytes.len()); + FrameDecoder::new(&*compressed_bytes).read_to_end(&mut inflated).map_err(|_| { + MetadataError::LoadFailure(format!( + "failed to decompress metadata: {}", + filename.display() + )) + })?; - slice_owned(inflated, Deref::deref) + slice_owned(inflated, Deref::deref) + } } CrateFlavor::Rmeta => { // mmap the file, because only a small fraction of it is read. From aa98c5d14e9ff560dd8539a3c4b61344c53fc8af Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 15 Jul 2023 17:35:36 +0000 Subject: [PATCH 039/113] Rewrite rmeta-rpass test to work with the new check for all crate sources being in sync --- tests/run-make/rmeta-preferred/Makefile | 16 ++++++++++++++++ .../rmeta-preferred/lib.rs} | 8 ++------ tests/run-make/rmeta-preferred/rmeta_aux.rs | 3 +++ tests/ui/rmeta/auxiliary/rmeta-rlib-rpass.rs | 8 -------- tests/ui/rmeta/auxiliary/rmeta-rmeta.rs | 9 --------- 5 files changed, 21 insertions(+), 23 deletions(-) create mode 100644 tests/run-make/rmeta-preferred/Makefile rename tests/{ui/rmeta/rmeta-rpass.rs => run-make/rmeta-preferred/lib.rs} (77%) create mode 100644 tests/run-make/rmeta-preferred/rmeta_aux.rs delete mode 100644 tests/ui/rmeta/auxiliary/rmeta-rlib-rpass.rs delete mode 100644 tests/ui/rmeta/auxiliary/rmeta-rmeta.rs diff --git a/tests/run-make/rmeta-preferred/Makefile b/tests/run-make/rmeta-preferred/Makefile new file mode 100644 index 000000000000..3bf12cced29a --- /dev/null +++ b/tests/run-make/rmeta-preferred/Makefile @@ -0,0 +1,16 @@ +# ignore-cross-compile +include ../tools.mk + +# Test that using rlibs and rmeta dep crates work together. Specifically, that +# there can be both an rmeta and an rlib file and rustc will prefer the rmeta +# file. +# +# This behavior is simply making sure this doesn't accidentally change; in this +# case we want to make sure that the rlib isn't being used as that would cause +# bugs in -Zbinary-dep-depinfo (see #68298). + +all: + $(RUSTC) rmeta_aux.rs --crate-type=rlib --emit link,metadata + $(RUSTC) lib.rs --crate-type=rlib --emit dep-info -Zbinary-dep-depinfo + $(CGREP) "librmeta_aux.rmeta" < $(TMPDIR)/lib.d + $(CGREP) -v "librmeta_aux.rlib" < $(TMPDIR)/lib.d diff --git a/tests/ui/rmeta/rmeta-rpass.rs b/tests/run-make/rmeta-preferred/lib.rs similarity index 77% rename from tests/ui/rmeta/rmeta-rpass.rs rename to tests/run-make/rmeta-preferred/lib.rs index 173a6a394eb0..d0b81a0628a8 100644 --- a/tests/ui/rmeta/rmeta-rpass.rs +++ b/tests/run-make/rmeta-preferred/lib.rs @@ -1,4 +1,3 @@ -// run-pass // Test that using rlibs and rmeta dep crates work together. Specifically, that // there can be both an rmeta and an rlib file and rustc will prefer the rmeta // file. @@ -7,12 +6,9 @@ // case we want to make sure that the rlib isn't being used as that would cause // bugs in -Zbinary-dep-depinfo (see #68298). -// aux-build:rmeta-rmeta.rs -// aux-build:rmeta-rlib-rpass.rs - extern crate rmeta_aux; use rmeta_aux::Foo; -pub fn main() { - let _ = Foo { field2: 42 }; +pub fn foo() { + let _ = Foo { field: 42 }; } diff --git a/tests/run-make/rmeta-preferred/rmeta_aux.rs b/tests/run-make/rmeta-preferred/rmeta_aux.rs new file mode 100644 index 000000000000..3f7a12b50546 --- /dev/null +++ b/tests/run-make/rmeta-preferred/rmeta_aux.rs @@ -0,0 +1,3 @@ +pub struct Foo { + pub field: i32, +} diff --git a/tests/ui/rmeta/auxiliary/rmeta-rlib-rpass.rs b/tests/ui/rmeta/auxiliary/rmeta-rlib-rpass.rs deleted file mode 100644 index f5e8c3d2a5c8..000000000000 --- a/tests/ui/rmeta/auxiliary/rmeta-rlib-rpass.rs +++ /dev/null @@ -1,8 +0,0 @@ -// no-prefer-dynamic - -#![crate_type="rlib"] -#![crate_name="rmeta_aux"] - -pub struct Foo { - pub field: i32, -} diff --git a/tests/ui/rmeta/auxiliary/rmeta-rmeta.rs b/tests/ui/rmeta/auxiliary/rmeta-rmeta.rs deleted file mode 100644 index 4a6d055a81fa..000000000000 --- a/tests/ui/rmeta/auxiliary/rmeta-rmeta.rs +++ /dev/null @@ -1,9 +0,0 @@ -// no-prefer-dynamic -// compile-flags: --emit=metadata - -#![crate_type="rlib"] -#![crate_name="rmeta_aux"] - -pub struct Foo { - pub field2: i32, -} From 8c9a8b63c9dbdd92991c3c3213fb52a03d8df757 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 19 Jul 2023 14:53:26 +0000 Subject: [PATCH 040/113] Fix review comments --- compiler/rustc_codegen_ssa/src/back/metadata.rs | 14 +++++++------- compiler/rustc_metadata/src/locator.rs | 12 +++++++----- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 9313b011cff6..c4bb51edade6 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -479,15 +479,15 @@ pub fn create_compressed_metadata_file( metadata: &EncodedMetadata, symbol_name: &str, ) -> Vec { - let mut compressed = rustc_metadata::METADATA_HEADER.to_vec(); - compressed.write_all(&(metadata.raw_data().len() as u32).to_be_bytes()).unwrap(); - compressed.extend(metadata.raw_data()); + let mut packed_metadata = rustc_metadata::METADATA_HEADER.to_vec(); + packed_metadata.write_all(&(metadata.raw_data().len() as u32).to_be_bytes()).unwrap(); + packed_metadata.extend(metadata.raw_data()); let Some(mut file) = create_object_file(sess) else { - return compressed.to_vec(); + return packed_metadata.to_vec(); }; if file.format() == BinaryFormat::Xcoff { - return create_compressed_metadata_file_for_xcoff(file, &compressed, symbol_name); + return create_compressed_metadata_file_for_xcoff(file, &packed_metadata, symbol_name); } let section = file.add_section( file.segment_name(StandardSegment::Data).to_vec(), @@ -501,14 +501,14 @@ pub fn create_compressed_metadata_file( } _ => {} }; - let offset = file.append_section_data(section, &compressed, 1); + let offset = file.append_section_data(section, &packed_metadata, 1); // For MachO and probably PE this is necessary to prevent the linker from throwing away the // .rustc section. For ELF this isn't necessary, but it also doesn't harm. file.add_symbol(Symbol { name: symbol_name.as_bytes().to_vec(), value: offset, - size: compressed.len() as u64, + size: packed_metadata.len() as u64, kind: SymbolKind::Data, scope: SymbolScope::Dynamic, weak: false, diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 30b7d33263ce..441959967623 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -511,7 +511,7 @@ impl<'a> CrateLocator<'a> { rlib: self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot)?, dylib: self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot)?, }; - Ok(slot.map(|(_, svh, metadata)| (svh, Library { source, metadata }))) + Ok(slot.map(|(svh, metadata, _)| (svh, Library { source, metadata }))) } fn needs_crate_flavor(&self, flavor: CrateFlavor) -> bool { @@ -535,11 +535,13 @@ impl<'a> CrateLocator<'a> { // read the metadata from it if `*slot` is `None`. If the metadata couldn't // be read, it is assumed that the file isn't a valid rust library (no // errors are emitted). + // + // The `PathBuf` in `slot` will only be used for diagnostic purposes. fn extract_one( &mut self, m: FxHashMap, flavor: CrateFlavor, - slot: &mut Option<(PathBuf, Svh, MetadataBlob)>, + slot: &mut Option<(Svh, MetadataBlob, PathBuf)>, ) -> Result, CrateError> { // If we are producing an rlib, and we've already loaded metadata, then // we should not attempt to discover further crate sources (unless we're @@ -595,7 +597,7 @@ impl<'a> CrateLocator<'a> { } }; // If we see multiple hashes, emit an error about duplicate candidates. - if slot.as_ref().is_some_and(|s| s.1 != hash) { + if slot.as_ref().is_some_and(|s| s.0 != hash) { if let Some(candidates) = err_data { return Err(CrateError::MultipleCandidates( self.crate_name, @@ -603,7 +605,7 @@ impl<'a> CrateLocator<'a> { candidates, )); } - err_data = Some(vec![slot.take().unwrap().0]); + err_data = Some(vec![slot.take().unwrap().2]); } if let Some(candidates) = &mut err_data { candidates.push(lib); @@ -636,7 +638,7 @@ impl<'a> CrateLocator<'a> { continue; } } - *slot = Some((lib.clone(), hash, metadata)); + *slot = Some((hash, metadata, lib.clone())); ret = Some((lib, kind)); } From fb31a1ac21833b205196128b7425f5c587cd883c Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Wed, 19 Jul 2023 11:50:29 -0400 Subject: [PATCH 041/113] avoid tls access while iterating through mpsc thread entries --- library/std/src/sync/mpmc/waker.rs | 46 +++++++++++++++++------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/library/std/src/sync/mpmc/waker.rs b/library/std/src/sync/mpmc/waker.rs index 4912ca4f8150..9aab1b9417ed 100644 --- a/library/std/src/sync/mpmc/waker.rs +++ b/library/std/src/sync/mpmc/waker.rs @@ -66,26 +66,32 @@ impl Waker { /// Attempts to find another thread's entry, select the operation, and wake it up. #[inline] pub(crate) fn try_select(&mut self) -> Option { - self.selectors - .iter() - .position(|selector| { - // Does the entry belong to a different thread? - selector.cx.thread_id() != current_thread_id() - && selector // Try selecting this operation. - .cx - .try_select(Selected::Operation(selector.oper)) - .is_ok() - && { - // Provide the packet. - selector.cx.store_packet(selector.packet); - // Wake the thread up. - selector.cx.unpark(); - true - } - }) - // Remove the entry from the queue to keep it clean and improve - // performance. - .map(|pos| self.selectors.remove(pos)) + if self.selectors.is_empty() { + None + } else { + let thread_id = current_thread_id(); + + self.selectors + .iter() + .position(|selector| { + // Does the entry belong to a different thread? + selector.cx.thread_id() != thread_id + && selector // Try selecting this operation. + .cx + .try_select(Selected::Operation(selector.oper)) + .is_ok() + && { + // Provide the packet. + selector.cx.store_packet(selector.packet); + // Wake the thread up. + selector.cx.unpark(); + true + } + }) + // Remove the entry from the queue to keep it clean and improve + // performance. + .map(|pos| self.selectors.remove(pos)) + } } /// Notifies all operations waiting to be ready. From c7bf20dfdcbedbba05445035bcabd4f706ba9e42 Mon Sep 17 00:00:00 2001 From: khei4 Date: Wed, 19 Jul 2023 17:00:06 +0900 Subject: [PATCH 042/113] address feedback from nikic and oli-obk https://github.com/rust-lang/rust/pull/113723/files use slice memcpy rather than strcpy and write it on stdout use println on failure Co-authored-by: Oli Scherer --- compiler/rustc_codegen_llvm/src/lib.rs | 31 +++++++++---------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 +-- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 14 +++++---- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index c03b21888244..b03543762103 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -46,6 +46,7 @@ use rustc_span::symbol::Symbol; use std::any::Any; use std::ffi::CStr; +use std::io::Write; mod back { pub mod archive; @@ -177,32 +178,30 @@ impl WriteBackendMethods for LlvmCodegenBackend { type ThinData = back::lto::ThinData; type ThinBuffer = back::lto::ThinBuffer; fn print_pass_timings(&self) { - let msg = unsafe { - let cstr = llvm::LLVMRustPrintPassTimings(); + unsafe { + let mut size = 0; + let cstr = llvm::LLVMRustPrintPassTimings(&mut size as *mut usize); if cstr.is_null() { - "failed to get pass timings".into() + println!("failed to get pass timings"); } else { - let timings = CStr::from_ptr(cstr).to_bytes(); - let timings = String::from_utf8_lossy(timings).to_string(); + let timings = std::slice::from_raw_parts(cstr as *const u8, size); + std::io::stdout().write_all(timings).unwrap(); libc::free(cstr as *mut _); - timings } - }; - println!("{}", msg); + } } fn print_statistics(&self) { - let msg = unsafe { - let cstr = llvm::LLVMRustPrintStatistics(); + unsafe { + let mut size = 0; + let cstr = llvm::LLVMRustPrintStatistics(&mut size as *mut usize); if cstr.is_null() { - "failed to get stats".into() + println!("failed to get pass stats"); } else { - let stats = CStr::from_ptr(cstr).to_bytes(); - let stats = String::from_utf8_lossy(stats).to_string(); + let stats = std::slice::from_raw_parts(cstr as *const u8, size); + std::io::stdout().write_all(stats).unwrap(); libc::free(cstr as *mut _); - stats } - }; - println!("{}", msg); + } } fn run_link( cgcx: &CodegenContext, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7cc79d859a38..fd88994c31c2 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1868,10 +1868,10 @@ extern "C" { pub fn LLVMRustGetLastError() -> *const c_char; /// Print the pass timings since static dtors aren't picking them up. - pub fn LLVMRustPrintPassTimings() -> *const c_char; + pub fn LLVMRustPrintPassTimings(size: *const size_t) -> *const c_char; /// Print the statistics since static dtors aren't picking them up. - pub fn LLVMRustPrintStatistics() -> *const c_char; + pub fn LLVMRustPrintStatistics(size: *const size_t) -> *const c_char; pub fn LLVMStructCreateNamed(C: &Context, Name: *const c_char) -> &Type; diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 695b8847a976..870a2e02cbaa 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -112,23 +112,25 @@ extern "C" void LLVMRustSetNormalizedTarget(LLVMModuleRef M, unwrap(M)->setTargetTriple(Triple::normalize(Triple)); } -extern "C" const char *LLVMRustPrintPassTimings(void) { +extern "C" const char *LLVMRustPrintPassTimings(size_t *Len) { std::string buf; raw_string_ostream SS(buf); TimerGroup::printAll(SS); SS.flush(); - char* CStr = (char*) malloc((buf.length() + 1) * sizeof(char)); - strcpy(CStr, buf.c_str()); + *Len = buf.length(); + char *CStr = (char *)malloc(*Len); + memcpy(CStr, buf.c_str(), *Len); return CStr; } -extern "C" const char *LLVMRustPrintStatistics(void) { +extern "C" const char *LLVMRustPrintStatistics(size_t *Len) { std::string buf; raw_string_ostream SS(buf); llvm::PrintStatistics(SS); SS.flush(); - char* CStr = (char*) malloc((buf.length() + 1) * sizeof(char)); - strcpy(CStr, buf.c_str()); + *Len = buf.length(); + char *CStr = (char *)malloc(*Len); + memcpy(CStr, buf.c_str(), *Len); return CStr; } From 2009b4a5cc1d097188da4396f423a076ac6282a1 Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Thu, 20 Jul 2023 17:46:32 +0900 Subject: [PATCH 043/113] Remove adjustments that used to be necessary for search's crate selector appearance (padding) to look identical on Firefox. New versions of Firefox appear to have changed behavior to agree with Chrome. --- src/librustdoc/html/static/css/rustdoc.css | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 94e778406f8b..b1de8c1529e7 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -776,7 +776,6 @@ table, } #crate-search { min-width: 115px; - /* keep these two in sync with "@-moz-document url-prefix()" below */ padding: 0 23px 0 4px; /* prevents the s */ -@-moz-document url-prefix() { - #crate-search { - padding-left: 0px; /* == 4px - 4px */ - padding-right: 19px; /* == 23px - 4px */ - } -} /* pseudo-element for holding the dropdown-arrow image; needs to be a separate thing so that we can apply CSS-filters to change the arrow color in themes */ #crate-search-div::after { From c7428d50520446ccd44ca89bbbf4ff7a4725570e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 20 Jul 2023 08:53:09 +0000 Subject: [PATCH 044/113] Monomorphize constants before inspecting them --- compiler/rustc_codegen_ssa/src/mir/constant.rs | 2 +- tests/ui/simd/shuffle.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 47a166e4e1be..babcf9bee249 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -65,7 +65,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &self, constant: &mir::Constant<'tcx>, ) -> Result>, ErrorHandled> { - let uv = match constant.literal { + let uv = match self.monomorphize(constant.literal) { mir::ConstantKind::Unevaluated(uv, _) => uv.shrink(), mir::ConstantKind::Ty(c) => match c.kind() { // A constant that came from a const generic but was then used as an argument to old-style diff --git a/tests/ui/simd/shuffle.rs b/tests/ui/simd/shuffle.rs index de41c9e25ddd..461243d48928 100644 --- a/tests/ui/simd/shuffle.rs +++ b/tests/ui/simd/shuffle.rs @@ -1,4 +1,7 @@ -//run-pass +// run-pass +// revisions: opt noopt +//[noopt] compile-flags: -Copt-level=0 +//[opt] compile-flags: -O #![feature(repr_simd, platform_intrinsics)] #![allow(incomplete_features)] #![feature(adt_const_params)] From fdaec57a28ab6a8cd60fd7a2842d366a9b452a2e Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 18 Jul 2023 17:03:22 +0200 Subject: [PATCH 045/113] XSimplifiedType to SimplifiedType::X --- .../src/coherence/orphan.rs | 10 +- compiler/rustc_middle/src/ty/fast_reject.rs | 112 +++++++++--------- .../src/solve/trait_goals.rs | 2 +- src/librustdoc/clean/inline.rs | 4 +- src/librustdoc/clean/types.rs | 51 ++++---- .../src/utils/internal_lints/invalid_paths.rs | 8 +- src/tools/clippy/clippy_utils/src/lib.rs | 47 ++++---- 7 files changed, 116 insertions(+), 118 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 05c78f570881..21ffbefcd081 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -568,10 +568,10 @@ fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: impl<'tcx> TypeVisitor> for DisableAutoTraitVisitor<'tcx> { type BreakTy = (); - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { let tcx = self.tcx; - if t != self.self_ty_root { - for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, t) { + if ty != self.self_ty_root { + for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, ty) { match tcx.impl_polarity(impl_def_id) { ImplPolarity::Negative => return ControlFlow::Break(()), ImplPolarity::Reservation => {} @@ -584,7 +584,7 @@ fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: } } - match t.kind() { + match ty.kind() { ty::Adt(def, args) if def.is_phantom_data() => args.visit_with(self), ty::Adt(def, args) => { // @lcnr: This is the only place where cycles can happen. We avoid this @@ -599,7 +599,7 @@ fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: ControlFlow::Continue(()) } - _ => t.super_visit_with(self), + _ => ty.super_visit_with(self), } } } diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index e86ff4d26aaa..deb4dcf1f760 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -6,35 +6,33 @@ use std::fmt::Debug; use std::hash::Hash; use std::iter; -use self::SimplifiedType::*; - /// See `simplify_type`. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] pub enum SimplifiedType { - BoolSimplifiedType, - CharSimplifiedType, - IntSimplifiedType(ty::IntTy), - UintSimplifiedType(ty::UintTy), - FloatSimplifiedType(ty::FloatTy), - AdtSimplifiedType(DefId), - ForeignSimplifiedType(DefId), - StrSimplifiedType, - ArraySimplifiedType, - SliceSimplifiedType, - RefSimplifiedType(Mutability), - PtrSimplifiedType(Mutability), - NeverSimplifiedType, - TupleSimplifiedType(usize), + Bool, + Char, + Int(ty::IntTy), + Uint(ty::UintTy), + Float(ty::FloatTy), + Adt(DefId), + Foreign(DefId), + Str, + Array, + Slice, + Ref(Mutability), + Ptr(Mutability), + Never, + Tuple(usize), /// A trait object, all of whose components are markers /// (e.g., `dyn Send + Sync`). - MarkerTraitObjectSimplifiedType, - TraitSimplifiedType(DefId), - ClosureSimplifiedType(DefId), - GeneratorSimplifiedType(DefId), - GeneratorWitnessSimplifiedType(usize), - GeneratorWitnessMIRSimplifiedType(DefId), - FunctionSimplifiedType(usize), - PlaceholderSimplifiedType, + MarkerTraitObject, + Trait(DefId), + Closure(DefId), + Generator(DefId), + GeneratorWitness(usize), + GeneratorWitnessMIR(DefId), + Function(usize), + Placeholder, } /// Generic parameters are pretty much just bound variables, e.g. @@ -110,34 +108,36 @@ pub fn simplify_type<'tcx>( treat_params: TreatParams, ) -> Option { match *ty.kind() { - ty::Bool => Some(BoolSimplifiedType), - ty::Char => Some(CharSimplifiedType), - ty::Int(int_type) => Some(IntSimplifiedType(int_type)), - ty::Uint(uint_type) => Some(UintSimplifiedType(uint_type)), - ty::Float(float_type) => Some(FloatSimplifiedType(float_type)), - ty::Adt(def, _) => Some(AdtSimplifiedType(def.did())), - ty::Str => Some(StrSimplifiedType), - ty::Array(..) => Some(ArraySimplifiedType), - ty::Slice(..) => Some(SliceSimplifiedType), - ty::RawPtr(ptr) => Some(PtrSimplifiedType(ptr.mutbl)), + ty::Bool => Some(SimplifiedType::Bool), + ty::Char => Some(SimplifiedType::Char), + 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.did())), + ty::Str => Some(SimplifiedType::Str), + ty::Array(..) => Some(SimplifiedType::Array), + ty::Slice(..) => Some(SimplifiedType::Slice), + ty::RawPtr(ptr) => Some(SimplifiedType::Ptr(ptr.mutbl)), ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() { Some(principal_def_id) if !tcx.trait_is_auto(principal_def_id) => { - Some(TraitSimplifiedType(principal_def_id)) + Some(SimplifiedType::Trait(principal_def_id)) } - _ => Some(MarkerTraitObjectSimplifiedType), + _ => Some(SimplifiedType::MarkerTraitObject), }, - ty::Ref(_, _, mutbl) => Some(RefSimplifiedType(mutbl)), - ty::FnDef(def_id, _) | ty::Closure(def_id, _) => Some(ClosureSimplifiedType(def_id)), - ty::Generator(def_id, _, _) => Some(GeneratorSimplifiedType(def_id)), - ty::GeneratorWitness(tys) => Some(GeneratorWitnessSimplifiedType(tys.skip_binder().len())), - ty::GeneratorWitnessMIR(def_id, _) => Some(GeneratorWitnessMIRSimplifiedType(def_id)), - ty::Never => Some(NeverSimplifiedType), - ty::Tuple(tys) => Some(TupleSimplifiedType(tys.len())), - ty::FnPtr(f) => Some(FunctionSimplifiedType(f.skip_binder().inputs().len())), - ty::Placeholder(..) => Some(PlaceholderSimplifiedType), + ty::Ref(_, _, mutbl) => Some(SimplifiedType::Ref(mutbl)), + ty::FnDef(def_id, _) | ty::Closure(def_id, _) => Some(SimplifiedType::Closure(def_id)), + ty::Generator(def_id, _, _) => Some(SimplifiedType::Generator(def_id)), + ty::GeneratorWitness(tys) => { + Some(SimplifiedType::GeneratorWitness(tys.skip_binder().len())) + } + ty::GeneratorWitnessMIR(def_id, _) => Some(SimplifiedType::GeneratorWitnessMIR(def_id)), + ty::Never => Some(SimplifiedType::Never), + ty::Tuple(tys) => Some(SimplifiedType::Tuple(tys.len())), + ty::FnPtr(f) => Some(SimplifiedType::Function(f.skip_binder().inputs().len())), + ty::Placeholder(..) => Some(SimplifiedType::Placeholder), ty::Param(_) => match treat_params { TreatParams::ForLookup | TreatParams::NextSolverLookup => { - Some(PlaceholderSimplifiedType) + Some(SimplifiedType::Placeholder) } TreatParams::AsCandidateKey => None, }, @@ -147,11 +147,13 @@ pub fn simplify_type<'tcx>( // // We will have to be careful with lazy normalization here. // FIXME(lazy_normalization): This is probably not right... - TreatParams::ForLookup if !ty.has_non_region_infer() => Some(PlaceholderSimplifiedType), - TreatParams::NextSolverLookup => Some(PlaceholderSimplifiedType), + TreatParams::ForLookup if !ty.has_non_region_infer() => { + Some(SimplifiedType::Placeholder) + } + TreatParams::NextSolverLookup => Some(SimplifiedType::Placeholder), TreatParams::ForLookup | TreatParams::AsCandidateKey => None, }, - ty::Foreign(def_id) => Some(ForeignSimplifiedType(def_id)), + ty::Foreign(def_id) => Some(SimplifiedType::Foreign(def_id)), ty::Bound(..) | ty::Infer(_) | ty::Error(_) => None, } } @@ -159,12 +161,12 @@ pub fn simplify_type<'tcx>( impl SimplifiedType { pub fn def(self) -> Option { match self { - AdtSimplifiedType(d) - | ForeignSimplifiedType(d) - | TraitSimplifiedType(d) - | ClosureSimplifiedType(d) - | GeneratorSimplifiedType(d) - | GeneratorWitnessMIRSimplifiedType(d) => Some(d), + SimplifiedType::Adt(d) + | SimplifiedType::Foreign(d) + | SimplifiedType::Trait(d) + | SimplifiedType::Closure(d) + | SimplifiedType::Generator(d) + | SimplifiedType::GeneratorWitnessMIR(d) => Some(d), _ => None, } } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index e7867eead159..29889614620d 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -686,7 +686,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Tuple(_) | ty::Adt(_, _) // FIXME: Handling opaques here is kinda sus. Especially because we - // simplify them to PlaceholderSimplifiedType. + // simplify them to SimplifiedType::Placeholder. | ty::Alias(ty::Opaque, _) => { let mut disqualifying_impl = None; self.tcx().for_each_relevant_impl_treating_projections( diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 5e2e2d249500..cfd9875e1c8f 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -12,6 +12,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, DefIdSet, LocalDefId}; use rustc_hir::Mutability; use rustc_metadata::creader::{CStore, LoadedMacro}; +use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Symbol}; @@ -314,9 +315,8 @@ pub(crate) fn build_impls( // * https://github.com/rust-lang/rust/pull/99917 — where the feature got used // * https://github.com/rust-lang/rust/issues/53487 — overall tracking issue for Error if tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { - use rustc_middle::ty::fast_reject::SimplifiedType::*; let type_ = - if tcx.is_trait(did) { TraitSimplifiedType(did) } else { AdtSimplifiedType(did) }; + if tcx.is_trait(did) { SimplifiedType::Trait(did) } else { SimplifiedType::Adt(did) }; for &did in tcx.incoherent_impls(type_) { build_impl(cx, did, attrs, ret); } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index cfe62407fd35..ddef165a0547 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1776,7 +1776,6 @@ impl PrimitiveType { } pub(crate) fn simplified_types() -> &'static SimplifiedTypes { - use ty::fast_reject::SimplifiedType::*; use ty::{FloatTy, IntTy, UintTy}; use PrimitiveType::*; static CELL: OnceCell = OnceCell::new(); @@ -1784,38 +1783,38 @@ impl PrimitiveType { let single = |x| iter::once(x).collect(); CELL.get_or_init(move || { map! { - Isize => single(IntSimplifiedType(IntTy::Isize)), - I8 => single(IntSimplifiedType(IntTy::I8)), - I16 => single(IntSimplifiedType(IntTy::I16)), - I32 => single(IntSimplifiedType(IntTy::I32)), - I64 => single(IntSimplifiedType(IntTy::I64)), - I128 => single(IntSimplifiedType(IntTy::I128)), - Usize => single(UintSimplifiedType(UintTy::Usize)), - U8 => single(UintSimplifiedType(UintTy::U8)), - U16 => single(UintSimplifiedType(UintTy::U16)), - U32 => single(UintSimplifiedType(UintTy::U32)), - U64 => single(UintSimplifiedType(UintTy::U64)), - U128 => single(UintSimplifiedType(UintTy::U128)), - F32 => single(FloatSimplifiedType(FloatTy::F32)), - F64 => single(FloatSimplifiedType(FloatTy::F64)), - Str => single(StrSimplifiedType), - Bool => single(BoolSimplifiedType), - Char => single(CharSimplifiedType), - Array => single(ArraySimplifiedType), - Slice => single(SliceSimplifiedType), + Isize => single(SimplifiedType::Int(IntTy::Isize)), + I8 => single(SimplifiedType::Int(IntTy::I8)), + I16 => single(SimplifiedType::Int(IntTy::I16)), + I32 => single(SimplifiedType::Int(IntTy::I32)), + I64 => single(SimplifiedType::Int(IntTy::I64)), + I128 => single(SimplifiedType::Int(IntTy::I128)), + Usize => single(SimplifiedType::Uint(UintTy::Usize)), + U8 => single(SimplifiedType::Uint(UintTy::U8)), + U16 => single(SimplifiedType::Uint(UintTy::U16)), + U32 => single(SimplifiedType::Uint(UintTy::U32)), + U64 => single(SimplifiedType::Uint(UintTy::U64)), + U128 => single(SimplifiedType::Uint(UintTy::U128)), + F32 => single(SimplifiedType::Float(FloatTy::F32)), + F64 => single(SimplifiedType::Float(FloatTy::F64)), + Str => single(SimplifiedType::Str), + Bool => single(SimplifiedType::Bool), + Char => single(SimplifiedType::Char), + Array => single(SimplifiedType::Array), + Slice => single(SimplifiedType::Slice), // FIXME: If we ever add an inherent impl for tuples // with different lengths, they won't show in rustdoc. // // Either manually update this arrayvec at this point // or start with a more complex refactoring. - Tuple => [TupleSimplifiedType(1), TupleSimplifiedType(2), TupleSimplifiedType(3)].into(), - Unit => single(TupleSimplifiedType(0)), - RawPointer => [PtrSimplifiedType(Mutability::Not), PtrSimplifiedType(Mutability::Mut)].into_iter().collect(), - Reference => [RefSimplifiedType(Mutability::Not), RefSimplifiedType(Mutability::Mut)].into_iter().collect(), + Tuple => [SimplifiedType::Tuple(1), SimplifiedType::Tuple(2), SimplifiedType::Tuple(3)].into(), + Unit => single(SimplifiedType::Tuple(0)), + RawPointer => [SimplifiedType::Ptr(Mutability::Not), SimplifiedType::Ptr(Mutability::Mut)].into_iter().collect(), + Reference => [SimplifiedType::Ref(Mutability::Not), SimplifiedType::Ref(Mutability::Mut)].into_iter().collect(), // FIXME: This will be wrong if we ever add inherent impls // for function pointers. - Fn => single(FunctionSimplifiedType(1)), - Never => single(NeverSimplifiedType), + Fn => single(SimplifiedType::Function(1)), + Never => single(SimplifiedType::Never), } }) } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 94b56304bcab..e4906944c8d0 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -74,10 +74,10 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { let lang_items = cx.tcx.lang_items(); // This list isn't complete, but good enough for our current list of paths. let incoherent_impls = [ - SimplifiedType::FloatSimplifiedType(FloatTy::F32), - SimplifiedType::FloatSimplifiedType(FloatTy::F64), - SimplifiedType::SliceSimplifiedType, - SimplifiedType::StrSimplifiedType, + SimplifiedType::Float(FloatTy::F32), + SimplifiedType::Float(FloatTy::F64), + SimplifiedType::Slice, + SimplifiedType::Str, ] .iter() .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter().copied()); diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 00e893fbdda8..035511e89125 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -100,10 +100,7 @@ use rustc_middle::mir::ConstantKind; use rustc_middle::ty as rustc_ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; -use rustc_middle::ty::fast_reject::SimplifiedType::{ - ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType, - PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType, -}; +use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ BorrowKind, ClosureKind, FloatTy, IntTy, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, UintTy, UpvarCapture, @@ -512,30 +509,30 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator + 'tcx { let ty = match name { - "bool" => BoolSimplifiedType, - "char" => CharSimplifiedType, - "str" => StrSimplifiedType, - "array" => ArraySimplifiedType, - "slice" => SliceSimplifiedType, + "bool" => SimplifiedType::Bool, + "char" => SimplifiedType::Char, + "str" => SimplifiedType::Str, + "array" => SimplifiedType::Array, + "slice" => SimplifiedType::Slice, // FIXME: rustdoc documents these two using just `pointer`. // // Maybe this is something we should do here too. - "const_ptr" => PtrSimplifiedType(Mutability::Not), - "mut_ptr" => PtrSimplifiedType(Mutability::Mut), - "isize" => IntSimplifiedType(IntTy::Isize), - "i8" => IntSimplifiedType(IntTy::I8), - "i16" => IntSimplifiedType(IntTy::I16), - "i32" => IntSimplifiedType(IntTy::I32), - "i64" => IntSimplifiedType(IntTy::I64), - "i128" => IntSimplifiedType(IntTy::I128), - "usize" => UintSimplifiedType(UintTy::Usize), - "u8" => UintSimplifiedType(UintTy::U8), - "u16" => UintSimplifiedType(UintTy::U16), - "u32" => UintSimplifiedType(UintTy::U32), - "u64" => UintSimplifiedType(UintTy::U64), - "u128" => UintSimplifiedType(UintTy::U128), - "f32" => FloatSimplifiedType(FloatTy::F32), - "f64" => FloatSimplifiedType(FloatTy::F64), + "const_ptr" => SimplifiedType::Ptr(Mutability::Not), + "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut), + "isize" => SimplifiedType::Int(IntTy::Isize), + "i8" => SimplifiedType::Int(IntTy::I8), + "i16" => SimplifiedType::Int(IntTy::I16), + "i32" => SimplifiedType::Int(IntTy::I32), + "i64" => SimplifiedType::Int(IntTy::I64), + "i128" => SimplifiedType::Int(IntTy::I128), + "usize" => SimplifiedType::Uint(UintTy::Usize), + "u8" => SimplifiedType::Uint(UintTy::U8), + "u16" => SimplifiedType::Uint(UintTy::U16), + "u32" => SimplifiedType::Uint(UintTy::U32), + "u64" => SimplifiedType::Uint(UintTy::U64), + "u128" => SimplifiedType::Uint(UintTy::U128), + "f32" => SimplifiedType::Float(FloatTy::F32), + "f64" => SimplifiedType::Float(FloatTy::F64), _ => return [].iter().copied(), }; From 2d99f40ec5ee10df8f7325e4e71d8249f8dacd92 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 18 Jul 2023 18:07:42 +0200 Subject: [PATCH 046/113] assembly: only consider blanket impls once --- .../src/solve/assembly/mod.rs | 273 +++++++++++++----- .../src/solve/project_goals.rs | 12 +- .../src/solve/trait_goals.rs | 9 +- ...mble-normalizing-self-ty-impl-ambiguity.rs | 25 ++ ...-normalizing-self-ty-impl-ambiguity.stderr | 23 ++ 5 files changed, 274 insertions(+), 68 deletions(-) create mode 100644 tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.rs create mode 100644 tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.stderr diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 1e7989988955..73cf15ff94b5 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -10,9 +10,11 @@ use rustc_infer::traits::util::elaborate; use rustc_infer::traits::Reveal; use rustc_middle::traits::solve::inspect::CandidateKind; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; -use rustc_middle::ty::fast_reject::TreatProjections; -use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams}; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{fast_reject, TypeFoldable}; +use rustc_span::ErrorGuaranteed; use std::fmt::Debug; pub(super) mod structural_traits; @@ -109,10 +111,10 @@ pub(super) trait GoalKind<'tcx>: fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId; - // Try equating an assumption predicate against a goal's predicate. If it - // holds, then execute the `then` callback, which should do any additional - // work, then produce a response (typically by executing - // [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]). + /// Try equating an assumption predicate against a goal's predicate. If it + /// holds, then execute the `then` callback, which should do any additional + /// work, then produce a response (typically by executing + /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]). fn probe_and_match_goal_against_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -120,9 +122,9 @@ pub(super) trait GoalKind<'tcx>: then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx>; - // Consider a clause, which consists of a "assumption" and some "requirements", - // to satisfy a goal. If the requirements hold, then attempt to satisfy our - // goal by equating it with the assumption. + /// Consider a clause, which consists of a "assumption" and some "requirements", + /// to satisfy a goal. If the requirements hold, then attempt to satisfy our + /// goal by equating it with the assumption. fn consider_implied_clause( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -149,9 +151,9 @@ pub(super) trait GoalKind<'tcx>: }) } - // Consider a clause specifically for a `dyn Trait` self type. This requires - // additionally checking all of the supertraits and object bounds to hold, - // since they're not implied by the well-formedness of the object type. + /// Consider a clause specifically for a `dyn Trait` self type. This requires + /// additionally checking all of the supertraits and object bounds to hold, + /// since they're not implied by the well-formedness of the object type. fn consider_object_bound_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -182,96 +184,106 @@ pub(super) trait GoalKind<'tcx>: impl_def_id: DefId, ) -> QueryResult<'tcx>; - // A type implements an `auto trait` if its components do as well. These components - // are given by built-in rules from [`instantiate_constituent_tys_for_auto_trait`]. + /// If the predicate contained an error, we want to avoid emitting unnecessary trait errors but + /// still want to emit errors for other trait goals. We have some special handling for this case. + /// + /// Trait goals always hold while projection goals never do. This is a bit arbitrary but prevents + /// incorrect normalization while hiding any trait errors. + fn consider_error_guaranteed_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + guar: ErrorGuaranteed, + ) -> QueryResult<'tcx>; + + /// A type implements an `auto trait` if its components do as well. These components + /// are given by built-in rules from [`instantiate_constituent_tys_for_auto_trait`]. fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A trait alias holds if the RHS traits and `where` clauses hold. + /// A trait alias holds if the RHS traits and `where` clauses hold. fn consider_trait_alias_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A type is `Copy` or `Clone` if its components are `Sized`. These components - // are given by built-in rules from [`instantiate_constituent_tys_for_sized_trait`]. + /// A type is `Copy` or `Clone` if its components are `Sized`. These components + /// are given by built-in rules from [`instantiate_constituent_tys_for_sized_trait`]. fn consider_builtin_sized_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. These - // components are given by built-in rules from [`instantiate_constituent_tys_for_copy_clone_trait`]. + /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. These + /// components are given by built-in rules from [`instantiate_constituent_tys_for_copy_clone_trait`]. fn consider_builtin_copy_clone_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A type is `PointerLike` if we can compute its layout, and that layout - // matches the layout of `usize`. + /// A type is `PointerLike` if we can compute its layout, and that layout + /// matches the layout of `usize`. fn consider_builtin_pointer_like_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A type is a `FnPtr` if it is of `FnPtr` type. + /// A type is a `FnPtr` if it is of `FnPtr` type. fn consider_builtin_fn_ptr_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn` - // family of traits where `A` is given by the signature of the type. + /// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn` + /// family of traits where `A` is given by the signature of the type. fn consider_builtin_fn_trait_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, kind: ty::ClosureKind, ) -> QueryResult<'tcx>; - // `Tuple` is implemented if the `Self` type is a tuple. + /// `Tuple` is implemented if the `Self` type is a tuple. fn consider_builtin_tuple_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // `Pointee` is always implemented. - // - // See the projection implementation for the `Metadata` types for all of - // the built-in types. For structs, the metadata type is given by the struct - // tail. + /// `Pointee` is always implemented. + /// + /// See the projection implementation for the `Metadata` types for all of + /// the built-in types. For structs, the metadata type is given by the struct + /// tail. fn consider_builtin_pointee_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A generator (that comes from an `async` desugaring) is known to implement - // `Future`, where `O` is given by the generator's return type - // that was computed during type-checking. + /// A generator (that comes from an `async` desugaring) is known to implement + /// `Future`, where `O` is given by the generator's return type + /// that was computed during type-checking. fn consider_builtin_future_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A generator (that doesn't come from an `async` desugaring) is known to - // implement `Generator`, given the resume, yield, - // and return types of the generator computed during type-checking. + /// A generator (that doesn't come from an `async` desugaring) is known to + /// implement `Generator`, given the resume, yield, + /// and return types of the generator computed during type-checking. fn consider_builtin_generator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // The most common forms of unsizing are array to slice, and concrete (Sized) - // type into a `dyn Trait`. ADTs and Tuples can also have their final field - // unsized if it's generic. + /// The most common forms of unsizing are array to slice, and concrete (Sized) + /// type into a `dyn Trait`. ADTs and Tuples can also have their final field + /// unsized if it's generic. fn consider_builtin_unsize_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or - // if `Trait2` is a (transitive) supertrait of `Trait2`. + /// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or + /// if `Trait2` is a (transitive) supertrait of `Trait2`. fn consider_builtin_dyn_upcast_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -299,35 +311,60 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, G>, ) -> Vec> { debug_assert_eq!(goal, self.resolve_vars_if_possible(goal)); + if let Some(ambig) = self.self_ty_infer_ambiguity_hack(goal) { + return ambig; + } - // HACK: `_: Trait` is ambiguous, because it may be satisfied via a builtin rule, - // object bound, alias bound, etc. We are unable to determine this until we can at - // least structurally resolve the type one layer. - if goal.predicate.self_ty().is_ty_var() { - return vec![Candidate { + let mut candidates = self.assemble_candidates_via_self_ty(goal); + + self.assemble_blanket_impl_candidates(goal, &mut candidates); + + self.assemble_param_env_candidates(goal, &mut candidates); + + candidates + } + + fn self_ty_infer_ambiguity_hack>( + &mut self, + goal: Goal<'tcx, G>, + ) -> Option>> { + goal.predicate.self_ty().is_ty_var().then(|| { + vec![Candidate { source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), result: self .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) .unwrap(), - }]; + }] + }) + } + + /// Assemble candidates which apply to the self type. This only looks at candidate which + /// apply to the specific self type and ignores all others. + /// + /// Returns `None` if the self type is still ambiguous. + fn assemble_candidates_via_self_ty>( + &mut self, + goal: Goal<'tcx, G>, + ) -> Vec> { + debug_assert_eq!(goal, self.resolve_vars_if_possible(goal)); + if let Some(ambig) = self.self_ty_infer_ambiguity_hack(goal) { + return ambig; } let mut candidates = Vec::new(); - self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates); - - self.assemble_impl_candidates(goal, &mut candidates); + self.assemble_non_blanket_impl_candidates(goal, &mut candidates); self.assemble_builtin_impl_candidates(goal, &mut candidates); - self.assemble_param_env_candidates(goal, &mut candidates); - self.assemble_alias_bound_candidates(goal, &mut candidates); self.assemble_object_bound_candidates(goal, &mut candidates); self.assemble_coherence_unknowable_candidates(goal, &mut candidates); + self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates); + candidates } @@ -385,7 +422,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // have a `Normalized` candidate. This doesn't work as long as we // use `CandidateSource` in winnowing. let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); - Ok(ecx.assemble_and_evaluate_candidates(goal)) + Ok(ecx.assemble_candidates_via_self_ty(goal)) }, ) }); @@ -396,22 +433,125 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } #[instrument(level = "debug", skip_all)] - fn assemble_impl_candidates>( + fn assemble_non_blanket_impl_candidates>( &mut self, goal: Goal<'tcx, G>, candidates: &mut Vec>, ) { let tcx = self.tcx(); - tcx.for_each_relevant_impl_treating_projections( - goal.predicate.trait_def_id(tcx), - goal.predicate.self_ty(), - TreatProjections::NextSolverLookup, - |impl_def_id| match G::consider_impl_candidate(self, goal, impl_def_id) { + let self_ty = goal.predicate.self_ty(); + let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx)); + let mut consider_impls_for_simplified_type = |simp| { + if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) { + for &impl_def_id in impls_for_type { + match G::consider_impl_candidate(self, goal, impl_def_id) { + Ok(result) => candidates + .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }), + Err(NoSolution) => (), + } + } + } + }; + + match self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::Generator(_, _, _) + | ty::Never + | ty::Tuple(_) => { + let simp = + fast_reject::simplify_type(tcx, self_ty, TreatParams::ForLookup).unwrap(); + consider_impls_for_simplified_type(simp); + } + + // HACK: For integer and float variables we have to manually look at all impls + // which have some integer or float as a self type. + ty::Infer(ty::IntVar(_)) => { + use ty::IntTy::*; + use ty::UintTy::*; + // This causes a compiler error if any new integer kinds are added. + let (I8 | I16 | I32 | I64 | I128 | Isize): ty::IntTy; + let (U8 | U16 | U32 | U64 | U128 | Usize): ty::UintTy; + let possible_integers = [ + // signed integers + SimplifiedType::Int(I8), + SimplifiedType::Int(I16), + SimplifiedType::Int(I32), + SimplifiedType::Int(I64), + SimplifiedType::Int(I128), + SimplifiedType::Int(Isize), + // unsigned integers + SimplifiedType::Uint(U8), + SimplifiedType::Uint(U16), + SimplifiedType::Uint(U32), + SimplifiedType::Uint(U64), + SimplifiedType::Uint(U128), + SimplifiedType::Uint(Usize), + ]; + for simp in possible_integers { + consider_impls_for_simplified_type(simp); + } + } + + ty::Infer(ty::FloatVar(_)) => { + // This causes a compiler error if any new float kinds are added. + let (ty::FloatTy::F32 | ty::FloatTy::F64); + let possible_floats = [ + SimplifiedType::Float(ty::FloatTy::F32), + SimplifiedType::Float(ty::FloatTy::F64), + ]; + + for simp in possible_floats { + consider_impls_for_simplified_type(simp); + } + } + + // The only traits applying to aliases and placeholders are blanket impls. + // + // Impls which apply to an alias after normalization are handled by + // `assemble_candidates_after_normalizing_self_ty`. + ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (), + + // FIXME: These should ideally not exist as a self type. It would be nice for + // the builtin auto trait impls of generators should instead directly recurse + // into the witness. + ty::GeneratorWitness(_) | ty::GeneratorWitnessMIR(_, _) => (), + + // These variants should not exist as a self type. + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Param(_) + | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"), + } + } + + fn assemble_blanket_impl_candidates>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec>, + ) { + let tcx = self.tcx(); + let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx)); + for &impl_def_id in trait_impls.blanket_impls() { + match G::consider_impl_candidate(self, goal, impl_def_id) { Ok(result) => candidates .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }), Err(NoSolution) => (), - }, - ); + } + } } #[instrument(level = "debug", skip_all)] @@ -420,8 +560,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, G>, candidates: &mut Vec>, ) { - let lang_items = self.tcx().lang_items(); - let trait_def_id = goal.predicate.trait_def_id(self.tcx()); + let tcx = self.tcx(); + let lang_items = tcx.lang_items(); + let trait_def_id = goal.predicate.trait_def_id(tcx); // N.B. When assembling built-in candidates for lang items that are also // `auto` traits, then the auto trait candidate that is assembled in @@ -430,9 +571,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // Instead of adding the logic here, it's a better idea to add it in // `EvalCtxt::disqualify_auto_trait_candidate_due_to_possible_impl` in // `solve::trait_goals` instead. - let result = if self.tcx().trait_is_auto(trait_def_id) { + let result = if let Err(guar) = goal.predicate.error_reported() { + G::consider_error_guaranteed_candidate(self, guar) + } else if tcx.trait_is_auto(trait_def_id) { G::consider_auto_trait_candidate(self, goal) - } else if self.tcx().trait_is_alias(trait_def_id) { + } else if tcx.trait_is_alias(trait_def_id) { G::consider_trait_alias_candidate(self, goal) } else if lang_items.sized_trait() == Some(trait_def_id) { G::consider_builtin_sized_candidate(self, goal) diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index d677fbdc7f42..222ed9939ba2 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -2,7 +2,6 @@ use crate::traits::specialization_graph; use super::assembly::{self, structural_traits}; use super::EvalCtxt; -use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; @@ -15,7 +14,7 @@ use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::ProjectionPredicate; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; -use rustc_span::{sym, DUMMY_SP}; +use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP}; impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] @@ -246,6 +245,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { }) } + /// Fail to normalize if the predicate contains an error, alternatively, we could normalize to `ty::Error` + /// and succeed. Can experiment with this to figure out what results in better error messages. + fn consider_error_guaranteed_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + _guar: ErrorGuaranteed, + ) -> QueryResult<'tcx> { + Err(NoSolution) + } + fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 29889614620d..930e62d63883 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -11,7 +11,7 @@ use rustc_middle::traits::Reveal; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use rustc_middle::ty::{TraitPredicate, TypeVisitableExt}; -use rustc_span::DUMMY_SP; +use rustc_span::{ErrorGuaranteed, DUMMY_SP}; impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn self_ty(self) -> Ty<'tcx> { @@ -78,6 +78,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }) } + fn consider_error_guaranteed_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + _guar: ErrorGuaranteed, + ) -> QueryResult<'tcx> { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + fn probe_and_match_goal_against_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, diff --git a/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.rs b/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.rs new file mode 100644 index 000000000000..727ce84ba359 --- /dev/null +++ b/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.rs @@ -0,0 +1,25 @@ +// compile-flags: -Ztrait-solver=next + +// Checks that we do not get ambiguity by considering an impl +// multiple times if we're able to normalize the self type. +trait Trait<'a> {} + +impl<'a, T: 'a> Trait<'a> for T {} + +fn impls_trait<'a, T: Trait<'a>>() {} + +trait Id { + type Assoc; +} +impl Id for T { + type Assoc = T; +} + +fn call() { + impls_trait::<::Assoc>(); +} + +fn main() { + call::<()>(); + impls_trait::<<<() as Id>::Assoc as Id>::Assoc>(); +} diff --git a/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.stderr b/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.stderr new file mode 100644 index 000000000000..91b635b35eb6 --- /dev/null +++ b/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.stderr @@ -0,0 +1,23 @@ +error[E0283]: type annotations needed: cannot satisfy `::Assoc: Trait<'_>` + --> $DIR/assemble-normalizing-self-ty-impl-ambiguity.rs:19:5 + | +LL | impls_trait::<::Assoc>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: cannot satisfy `::Assoc: Trait<'_>` +note: required by a bound in `impls_trait` + --> $DIR/assemble-normalizing-self-ty-impl-ambiguity.rs:9:23 + | +LL | fn impls_trait<'a, T: Trait<'a>>() {} + | ^^^^^^^^^ required by this bound in `impls_trait` + +error[E0282]: type annotations needed + --> $DIR/assemble-normalizing-self-ty-impl-ambiguity.rs:24:5 + | +LL | impls_trait::<<<() as Id>::Assoc as Id>::Assoc>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls_trait` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0282, E0283. +For more information about an error, try `rustc --explain E0282`. From aa28b77b5a362a70e4c1e4f99047f903f735954d Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 18 Jul 2023 18:15:14 +0200 Subject: [PATCH 047/113] add FIXME --- compiler/rustc_middle/src/ty/fast_reject.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index deb4dcf1f760..668aa4521c10 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -62,6 +62,9 @@ pub enum TreatParams { /// correct mode for *lookup*, as during candidate selection. /// /// N.B. during deep rejection, this acts identically to `ForLookup`. + /// + /// FIXME(-Ztrait-solver=next): Remove this variant and cleanup + /// the code. NextSolverLookup, } From 7c97a76b76120bd982762a982d4254ece32c43dc Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 18 Jul 2023 18:18:32 +0200 Subject: [PATCH 048/113] re-add comment --- compiler/rustc_trait_selection/src/solve/assembly/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 73cf15ff94b5..68e931aac8c1 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -324,6 +324,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidates } + /// HACK: `_: Trait` is ambiguous, because it may be satisfied via a builtin rule, + /// object bound, alias bound, etc. We are unable to determine this until we can at + /// least structurally resolve the type one layer. + /// + /// It would also require us to consider all impls of the trait, which is both pretty + /// bad for perf and would also constrain the self type if there is just a single impl. fn self_ty_infer_ambiguity_hack>( &mut self, goal: Goal<'tcx, G>, From 7de9b654b7888ee387a9907f7a5f9b824ca053be Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 17 Jul 2023 17:42:59 +0000 Subject: [PATCH 049/113] Avoid another gha group nesting --- src/bootstrap/test.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 44b8c2d8c013..c69b21488d23 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -2087,10 +2087,11 @@ impl Step for ErrorIndex { let mut tool = tool::ErrorIndex::command(builder); tool.arg("markdown").arg(&output); - let _guard = + let guard = builder.msg(Kind::Test, compiler.stage, "error-index", compiler.host, compiler.host); let _time = util::timeit(&builder); builder.run_quiet(&mut tool); + drop(guard); // The tests themselves need to link to std, so make sure it is // available. builder.ensure(compile::Std::new(compiler, compiler.host)); From 2062f2ca8261b521caa9c34c195bf472dcb59e98 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 20 Jul 2023 10:40:59 +0200 Subject: [PATCH 050/113] review --- .../src/solve/assembly/mod.rs | 8 +++---- ...mble-normalizing-self-ty-impl-ambiguity.rs | 2 ++ ...-normalizing-self-ty-impl-ambiguity.stderr | 23 ------------------- .../dont-normalize-proj-with-error.rs | 22 ++++++++++++++++++ .../dont-normalize-proj-with-error.stderr | 9 ++++++++ 5 files changed, 37 insertions(+), 27 deletions(-) delete mode 100644 tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.stderr create mode 100644 tests/ui/traits/new-solver/dont-normalize-proj-with-error.rs create mode 100644 tests/ui/traits/new-solver/dont-normalize-proj-with-error.stderr diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 68e931aac8c1..b661ff481a0a 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -311,7 +311,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, G>, ) -> Vec> { debug_assert_eq!(goal, self.resolve_vars_if_possible(goal)); - if let Some(ambig) = self.self_ty_infer_ambiguity_hack(goal) { + if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) { return ambig; } @@ -324,13 +324,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidates } - /// HACK: `_: Trait` is ambiguous, because it may be satisfied via a builtin rule, + /// `?0: Trait` is ambiguous, because it may be satisfied via a builtin rule, /// object bound, alias bound, etc. We are unable to determine this until we can at /// least structurally resolve the type one layer. /// /// It would also require us to consider all impls of the trait, which is both pretty /// bad for perf and would also constrain the self type if there is just a single impl. - fn self_ty_infer_ambiguity_hack>( + fn assemble_self_ty_infer_ambiguity_response>( &mut self, goal: Goal<'tcx, G>, ) -> Option>> { @@ -353,7 +353,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, G>, ) -> Vec> { debug_assert_eq!(goal, self.resolve_vars_if_possible(goal)); - if let Some(ambig) = self.self_ty_infer_ambiguity_hack(goal) { + if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) { return ambig; } diff --git a/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.rs b/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.rs index 727ce84ba359..826e8c1e0b13 100644 --- a/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.rs +++ b/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.rs @@ -1,7 +1,9 @@ // compile-flags: -Ztrait-solver=next +// check-pass // Checks that we do not get ambiguity by considering an impl // multiple times if we're able to normalize the self type. + trait Trait<'a> {} impl<'a, T: 'a> Trait<'a> for T {} diff --git a/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.stderr b/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.stderr deleted file mode 100644 index 91b635b35eb6..000000000000 --- a/tests/ui/traits/new-solver/assembly/assemble-normalizing-self-ty-impl-ambiguity.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0283]: type annotations needed: cannot satisfy `::Assoc: Trait<'_>` - --> $DIR/assemble-normalizing-self-ty-impl-ambiguity.rs:19:5 - | -LL | impls_trait::<::Assoc>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: cannot satisfy `::Assoc: Trait<'_>` -note: required by a bound in `impls_trait` - --> $DIR/assemble-normalizing-self-ty-impl-ambiguity.rs:9:23 - | -LL | fn impls_trait<'a, T: Trait<'a>>() {} - | ^^^^^^^^^ required by this bound in `impls_trait` - -error[E0282]: type annotations needed - --> $DIR/assemble-normalizing-self-ty-impl-ambiguity.rs:24:5 - | -LL | impls_trait::<<<() as Id>::Assoc as Id>::Assoc>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls_trait` - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0282, E0283. -For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/new-solver/dont-normalize-proj-with-error.rs b/tests/ui/traits/new-solver/dont-normalize-proj-with-error.rs new file mode 100644 index 000000000000..19a6fa990ff1 --- /dev/null +++ b/tests/ui/traits/new-solver/dont-normalize-proj-with-error.rs @@ -0,0 +1,22 @@ +// compile-flags: -Ztrait-solver=next + +// Test that we don't incorrectly leak unconstrained inference variables +// if the projection contained an error. This caused an ICE in writeback. + +trait Mirror { + type Assoc: ?Sized; +} + +struct Wrapper(T); +impl Mirror for Wrapper { + type Assoc = T; +} + +fn mirror(_: W) -> Box { todo!() } + +fn type_error() -> TypeError { todo!() } +//~^ ERROR cannot find type `TypeError` in this scope + +fn main() { + let x = mirror(type_error()); +} diff --git a/tests/ui/traits/new-solver/dont-normalize-proj-with-error.stderr b/tests/ui/traits/new-solver/dont-normalize-proj-with-error.stderr new file mode 100644 index 000000000000..5a7459ec1fde --- /dev/null +++ b/tests/ui/traits/new-solver/dont-normalize-proj-with-error.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `TypeError` in this scope + --> $DIR/dont-normalize-proj-with-error.rs:17:20 + | +LL | fn type_error() -> TypeError { todo!() } + | ^^^^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`. From 5c75bc5317b4326bdc3d8a4708d0602bd0ec219d Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 20 Jul 2023 11:26:22 +0200 Subject: [PATCH 051/113] update doc comments --- .../src/solve/assembly/mod.rs | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index b661ff481a0a..6920e790e71a 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -184,18 +184,21 @@ pub(super) trait GoalKind<'tcx>: impl_def_id: DefId, ) -> QueryResult<'tcx>; - /// If the predicate contained an error, we want to avoid emitting unnecessary trait errors but - /// still want to emit errors for other trait goals. We have some special handling for this case. + /// If the predicate contained an error, we want to avoid emitting unnecessary trait + /// errors but still want to emit errors for other trait goals. We have some special + /// handling for this case. /// - /// Trait goals always hold while projection goals never do. This is a bit arbitrary but prevents - /// incorrect normalization while hiding any trait errors. + /// Trait goals always hold while projection goals never do. This is a bit arbitrary + /// but prevents incorrect normalization while hiding any trait errors. fn consider_error_guaranteed_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, guar: ErrorGuaranteed, ) -> QueryResult<'tcx>; - /// A type implements an `auto trait` if its components do as well. These components - /// are given by built-in rules from [`instantiate_constituent_tys_for_auto_trait`]. + /// A type implements an `auto trait` if its components do as well. + /// + /// These components are given by built-in rules from + /// [`structural_traits::instantiate_constituent_tys_for_auto_trait`]. fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -207,15 +210,19 @@ pub(super) trait GoalKind<'tcx>: goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - /// A type is `Copy` or `Clone` if its components are `Sized`. These components - /// are given by built-in rules from [`instantiate_constituent_tys_for_sized_trait`]. + /// A type is `Copy` or `Clone` if its components are `Sized`. + /// + /// These components are given by built-in rules from + /// [`structural_traits::instantiate_constituent_tys_for_sized_trait`]. fn consider_builtin_sized_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. These - /// components are given by built-in rules from [`instantiate_constituent_tys_for_copy_clone_trait`]. + /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. + /// + /// These components are given by built-in rules from + /// [`structural_traits::instantiate_constituent_tys_for_copy_clone_trait`]. fn consider_builtin_copy_clone_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, From 41a73d8251553afa39c7082b44b2781ca4898578 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 20 Jul 2023 18:40:15 +0200 Subject: [PATCH 052/113] clarify MIR uninit vs LLVM undef/poison --- compiler/rustc_codegen_ssa/src/traits/consts.rs | 6 ++++++ compiler/rustc_const_eval/src/interpret/operand.rs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index d6e9bfce1a4f..822c19155e3b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -5,7 +5,13 @@ use rustc_target::abi; pub trait ConstMethods<'tcx>: BackendTypes { // Constant constructors fn const_null(&self, t: Self::Type) -> Self::Value; + /// Generate an uninitialized value (matching uninitialized memory in MIR). + /// Whether memory is initialized or not is tracked byte-for-byte. fn const_undef(&self, t: Self::Type) -> Self::Value; + /// Generate a fake value. Poison always affects the entire value, even if just a single byte is + /// poison. This can only be used in codepaths that are already UB, i.e., UB-free Rust code + /// (including code that e.g. copies uninit memory with `MaybeUninit`) can never encounter a + /// poison value. fn const_poison(&self, t: Self::Type) -> Self::Value; fn const_int(&self, t: Self::Type, i: i64) -> Self::Value; fn const_uint(&self, t: Self::Type, i: u64) -> Self::Value; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index da45fdca1a12..47383c0eabc3 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -31,7 +31,7 @@ pub enum Immediate { /// A pair of two scalar value (must have `ScalarPair` ABI where both fields are /// `Scalar::Initialized`). ScalarPair(Scalar, Scalar), - /// A value of fully uninitialized memory. Can have and size and layout. + /// A value of fully uninitialized memory. Can have arbitrary size and layout. Uninit, } From 11dcd1d3d7e805a555a41992312137700ee370a1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 24 Apr 2023 17:57:41 -0700 Subject: [PATCH 053/113] Add test of --print KIND=PATH --- tests/run-make/print-cfg/Makefile | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/run-make/print-cfg/Makefile b/tests/run-make/print-cfg/Makefile index 126f5768c90f..6b153e5b54ed 100644 --- a/tests/run-make/print-cfg/Makefile +++ b/tests/run-make/print-cfg/Makefile @@ -2,7 +2,7 @@ include ../tools.mk -all: default +all: default output_to_file $(RUSTC) --target x86_64-pc-windows-gnu --print cfg | $(CGREP) windows $(RUSTC) --target x86_64-pc-windows-gnu --print cfg | $(CGREP) x86_64 $(RUSTC) --target i686-pc-windows-msvc --print cfg | $(CGREP) msvc @@ -11,6 +11,23 @@ all: default $(RUSTC) --target arm-unknown-linux-gnueabihf --print cfg | $(CGREP) target_abi= $(RUSTC) --target arm-unknown-linux-gnueabihf --print cfg | $(CGREP) eabihf +output_to_file: + # Backend-independent, printed by rustc_driver_impl/src/lib.rs + $(RUSTC) --target x86_64-pc-windows-gnu --print cfg=$(TMPDIR)/cfg.txt + $(CGREP) windows < $(TMPDIR)/cfg.txt + + # Printed from CodegenBackend trait impl in rustc_codegen_llvm/src/lib.rs + $(RUSTC) --print relocation-models=$(TMPDIR)/relocation-models.txt + $(CGREP) dynamic-no-pic < $(TMPDIR)/relocation-models.txt + + # Printed by compiler/rustc_codegen_llvm/src/llvm_util.rs + $(RUSTC) --target wasm32-unknown-unknown --print target-features=$(TMPDIR)/target-features.txt + $(CGREP) reference-types < $(TMPDIR)/target-features.txt + + # Printed by C++ code in rustc_llvm/llvm-wrapper/PassWrapper.cpp + $(RUSTC) --target wasm32-unknown-unknown --print target-cpus=$(TMPDIR)/target-cpus.txt + $(CGREP) generic < $(TMPDIR)/target-cpus.txt + ifdef IS_WINDOWS default: $(RUSTC) --print cfg | $(CGREP) windows From c0dc0c6875e1907f1a8d690bae800c9d0c89b4f1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 16 Jul 2023 17:20:28 -0700 Subject: [PATCH 054/113] Store individual output file name with every PrintRequest --- compiler/rustc_codegen_llvm/src/lib.rs | 16 ++--- compiler/rustc_codegen_llvm/src/llvm_util.rs | 10 +-- compiler/rustc_codegen_ssa/src/back/link.rs | 6 +- .../rustc_codegen_ssa/src/traits/backend.rs | 2 +- compiler/rustc_driver_impl/src/lib.rs | 14 ++-- compiler/rustc_session/src/config.rs | 69 ++++++++++--------- 6 files changed, 61 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 24ba28bbc82c..38a32ac845d2 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -40,7 +40,7 @@ use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{OptLevel, OutputFilenames, PrintRequest}; +use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; use rustc_session::Session; use rustc_span::symbol::Symbol; @@ -262,9 +262,9 @@ impl CodegenBackend for LlvmCodegenBackend { |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true) } - fn print(&self, req: PrintRequest, sess: &Session) { - match req { - PrintRequest::RelocationModels => { + fn print(&self, req: &PrintRequest, sess: &Session) { + match req.kind { + PrintKind::RelocationModels => { println!("Available relocation models:"); for name in &[ "static", @@ -280,21 +280,21 @@ impl CodegenBackend for LlvmCodegenBackend { } println!(); } - PrintRequest::CodeModels => { + PrintKind::CodeModels => { println!("Available code models:"); for name in &["tiny", "small", "kernel", "medium", "large"] { println!(" {}", name); } println!(); } - PrintRequest::TlsModels => { + PrintKind::TlsModels => { println!("Available TLS models:"); for name in &["global-dynamic", "local-dynamic", "initial-exec", "local-exec"] { println!(" {}", name); } println!(); } - PrintRequest::StackProtectorStrategies => { + PrintKind::StackProtectorStrategies => { println!( r#"Available stack protector strategies: all @@ -319,7 +319,7 @@ impl CodegenBackend for LlvmCodegenBackend { "# ); } - req => llvm_util::print(req, sess), + _other => llvm_util::print(req, sess), } } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 03be0654b50b..84d9acf45de5 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -12,7 +12,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::small_c_str::SmallCStr; use rustc_fs_util::path_to_c_string; use rustc_middle::bug; -use rustc_session::config::PrintRequest; +use rustc_session::config::{PrintKind, PrintRequest}; use rustc_session::Session; use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy}; @@ -400,11 +400,11 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) { println!("and may be renamed or removed in a future version of LLVM or rustc.\n"); } -pub(crate) fn print(req: PrintRequest, sess: &Session) { +pub(crate) fn print(req: &PrintRequest, sess: &Session) { require_inited(); let tm = create_informational_target_machine(sess); - match req { - PrintRequest::TargetCPUs => { + match req.kind { + PrintKind::TargetCPUs => { // SAFETY generate a C compatible string from a byte slice to pass // the target CPU name into LLVM, the lifetime of the reference is // at least as long as the C function @@ -412,7 +412,7 @@ pub(crate) fn print(req: PrintRequest, sess: &Session) { .unwrap_or_else(|e| bug!("failed to convert to cstring: {}", e)); unsafe { llvm::LLVMRustPrintTargetCPUs(tm, cpu_cstring.as_ptr()) }; } - PrintRequest::TargetFeatures => print_target_features(sess, tm), + PrintKind::TargetFeatures => print_target_features(sess, tm), _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), } } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 0dfb41f42f0d..bb8a520321a1 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -13,7 +13,7 @@ use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, Strip}; -use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind}; +use rustc_session::config::{OutputFilenames, OutputType, PrintKind, SplitDwarfKind}; use rustc_session::cstore::DllImport; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; use rustc_session::search_paths::PathKind; @@ -596,7 +596,7 @@ fn link_staticlib<'a>( all_native_libs.extend_from_slice(&codegen_results.crate_info.used_libraries); - if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) { + if sess.opts.prints.iter().any(|print| print.kind == PrintKind::NativeStaticLibs) { print_native_static_libs(sess, &all_native_libs, &all_rust_dylibs); } @@ -744,7 +744,7 @@ fn link_natively<'a>( cmd.env_remove(k.as_ref()); } - if sess.opts.prints.contains(&PrintRequest::LinkArgs) { + if sess.opts.prints.iter().any(|print| print.kind == PrintKind::LinkArgs) { println!("{:?}", &cmd); } diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index b3c9ecf8b938..f10bbbeb97cc 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -61,7 +61,7 @@ pub trait CodegenBackend { fn locale_resource(&self) -> &'static str; fn init(&self, _sess: &Session) {} - fn print(&self, _req: PrintRequest, _sess: &Session) {} + fn print(&self, _req: &PrintRequest, _sess: &Session) {} fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec { vec![] } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 25c043149e81..27dfb6c3bca6 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -35,9 +35,7 @@ use rustc_interface::{interface, Queries}; use rustc_lint::LintStore; use rustc_metadata::locator; use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS}; -use rustc_session::config::{ - ErrorOutputType, Input, OutFileName, OutputType, PrintRequest, TrimmedDefPaths, -}; +use rustc_session::config::{ErrorOutputType, Input, OutFileName, OutputType, TrimmedDefPaths}; use rustc_session::cstore::MetadataLoader; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; @@ -714,10 +712,10 @@ fn print_crate_info( sess: &Session, parse_attrs: bool, ) -> Compilation { - use rustc_session::config::PrintRequest::*; + use rustc_session::config::PrintKind::*; // NativeStaticLibs and LinkArgs are special - printed during linking // (empty iterator returns true) - if sess.opts.prints.iter().all(|&p| p == NativeStaticLibs || p == LinkArgs) { + if sess.opts.prints.iter().all(|p| p.kind == NativeStaticLibs || p.kind == LinkArgs) { return Compilation::Continue; } @@ -734,7 +732,7 @@ fn print_crate_info( None }; for req in &sess.opts.prints { - match *req { + match req.kind { TargetList => { let mut targets = rustc_target::spec::TARGETS.to_vec(); targets.sort_unstable(); @@ -761,7 +759,7 @@ fn print_crate_info( }; let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess); let id = rustc_session::output::find_crate_name(sess, attrs); - if *req == PrintRequest::CrateName { + if req.kind == CrateName { safe_println!("{id}"); continue; } @@ -817,7 +815,7 @@ fn print_crate_info( | TargetCPUs | StackProtectorStrategies | TargetFeatures => { - codegen_backend.print(*req, sess); + codegen_backend.print(req, sess); } // Any output here interferes with Cargo's parsing of other printed output NativeStaticLibs => {} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 593983f117d0..261d3529b141 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -710,8 +710,14 @@ impl ExternEntry { } } +#[derive(Clone, PartialEq, Debug)] +pub struct PrintRequest { + pub kind: PrintKind, + pub out: OutFileName, +} + #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum PrintRequest { +pub enum PrintKind { FileNames, Sysroot, TargetLibdir, @@ -2091,41 +2097,41 @@ fn collect_print_requests( ) -> Vec { let mut prints = Vec::::new(); if cg.target_cpu.as_ref().is_some_and(|s| s == "help") { - prints.push(PrintRequest::TargetCPUs); + prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout }); cg.target_cpu = None; }; if cg.target_feature == "help" { - prints.push(PrintRequest::TargetFeatures); + prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout }); cg.target_feature = String::new(); } - const PRINT_REQUESTS: &[(&str, PrintRequest)] = &[ - ("crate-name", PrintRequest::CrateName), - ("file-names", PrintRequest::FileNames), - ("sysroot", PrintRequest::Sysroot), - ("target-libdir", PrintRequest::TargetLibdir), - ("cfg", PrintRequest::Cfg), - ("calling-conventions", PrintRequest::CallingConventions), - ("target-list", PrintRequest::TargetList), - ("target-cpus", PrintRequest::TargetCPUs), - ("target-features", PrintRequest::TargetFeatures), - ("relocation-models", PrintRequest::RelocationModels), - ("code-models", PrintRequest::CodeModels), - ("tls-models", PrintRequest::TlsModels), - ("native-static-libs", PrintRequest::NativeStaticLibs), - ("stack-protector-strategies", PrintRequest::StackProtectorStrategies), - ("target-spec-json", PrintRequest::TargetSpec), - ("all-target-specs-json", PrintRequest::AllTargetSpecs), - ("link-args", PrintRequest::LinkArgs), - ("split-debuginfo", PrintRequest::SplitDebuginfo), - ("deployment-target", PrintRequest::DeploymentTarget), + const PRINT_KINDS: &[(&str, PrintKind)] = &[ + ("crate-name", PrintKind::CrateName), + ("file-names", PrintKind::FileNames), + ("sysroot", PrintKind::Sysroot), + ("target-libdir", PrintKind::TargetLibdir), + ("cfg", PrintKind::Cfg), + ("calling-conventions", PrintKind::CallingConventions), + ("target-list", PrintKind::TargetList), + ("target-cpus", PrintKind::TargetCPUs), + ("target-features", PrintKind::TargetFeatures), + ("relocation-models", PrintKind::RelocationModels), + ("code-models", PrintKind::CodeModels), + ("tls-models", PrintKind::TlsModels), + ("native-static-libs", PrintKind::NativeStaticLibs), + ("stack-protector-strategies", PrintKind::StackProtectorStrategies), + ("target-spec-json", PrintKind::TargetSpec), + ("all-target-specs-json", PrintKind::AllTargetSpecs), + ("link-args", PrintKind::LinkArgs), + ("split-debuginfo", PrintKind::SplitDebuginfo), + ("deployment-target", PrintKind::DeploymentTarget), ]; prints.extend(matches.opt_strs("print").into_iter().map(|req| { - match PRINT_REQUESTS.iter().find(|&&(name, _)| name == req) { - Some((_, PrintRequest::TargetSpec)) => { + let kind = match PRINT_KINDS.iter().find(|&&(name, _)| name == req) { + Some((_, PrintKind::TargetSpec)) => { if unstable_opts.unstable_options { - PrintRequest::TargetSpec + PrintKind::TargetSpec } else { handler.early_error( "the `-Z unstable-options` flag must also be passed to \ @@ -2133,9 +2139,9 @@ fn collect_print_requests( ); } } - Some((_, PrintRequest::AllTargetSpecs)) => { + Some((_, PrintKind::AllTargetSpecs)) => { if unstable_opts.unstable_options { - PrintRequest::AllTargetSpecs + PrintKind::AllTargetSpecs } else { handler.early_error( "the `-Z unstable-options` flag must also be passed to \ @@ -2143,16 +2149,17 @@ fn collect_print_requests( ); } } - Some(&(_, print_request)) => print_request, + Some(&(_, print_kind)) => print_kind, None => { let prints = - PRINT_REQUESTS.iter().map(|(name, _)| format!("`{name}`")).collect::>(); + PRINT_KINDS.iter().map(|(name, _)| format!("`{name}`")).collect::>(); let prints = prints.join(", "); handler.early_error(format!( "unknown print request `{req}`. Valid print requests are: {prints}" )); } - } + }; + PrintRequest { kind, out: OutFileName::Stdout } })); prints From f72bdb150180f3fb9ccd6447f13e9e37b3e33e79 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 16 Jul 2023 17:58:46 -0700 Subject: [PATCH 055/113] Parse --print KIND=PATH command line syntax --- compiler/rustc_session/src/config.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 261d3529b141..738625485c49 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2011,13 +2011,7 @@ fn parse_output_types( if !unstable_opts.parse_only { for list in matches.opt_strs("emit") { for output_type in list.split(',') { - let (shorthand, path) = match output_type.split_once('=') { - None => (output_type, None), - Some((shorthand, "-")) => (shorthand, Some(OutFileName::Stdout)), - Some((shorthand, path)) => { - (shorthand, Some(OutFileName::Real(PathBuf::from(path)))) - } - }; + let (shorthand, path) = split_out_file_name(output_type); let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { handler.early_error(format!( "unknown emission type: `{shorthand}` - expected one of: {display}", @@ -2034,6 +2028,14 @@ fn parse_output_types( OutputTypes(output_types) } +fn split_out_file_name(arg: &str) -> (&str, Option) { + match arg.split_once('=') { + None => (arg, None), + Some((kind, "-")) => (kind, Some(OutFileName::Stdout)), + Some((kind, path)) => (kind, Some(OutFileName::Real(PathBuf::from(path)))), + } +} + fn should_override_cgus_and_disable_thinlto( handler: &EarlyErrorHandler, output_types: &OutputTypes, @@ -2128,6 +2130,8 @@ fn collect_print_requests( ]; prints.extend(matches.opt_strs("print").into_iter().map(|req| { + let (req, out) = split_out_file_name(&req); + let kind = match PRINT_KINDS.iter().find(|&&(name, _)| name == req) { Some((_, PrintKind::TargetSpec)) => { if unstable_opts.unstable_options { @@ -2159,7 +2163,9 @@ fn collect_print_requests( )); } }; - PrintRequest { kind, out: OutFileName::Stdout } + + let out = out.unwrap_or(OutFileName::Stdout); + PrintRequest { kind, out } })); prints From 32cac2e0024aa4d02661859f9bfc69d33e6e8ba9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 16 Jul 2023 19:06:04 -0700 Subject: [PATCH 056/113] Disallow overlapping prints to the same location --- compiler/rustc_session/src/config.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 738625485c49..ee871dc8005c 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2129,6 +2129,12 @@ fn collect_print_requests( ("deployment-target", PrintKind::DeploymentTarget), ]; + // We disallow reusing the same path in multiple prints, such as `--print + // cfg=output.txt --print link-args=output.txt`, because outputs are printed + // by disparate pieces of the compiler, and keeping track of which files + // need to be overwritten vs appended to is annoying. + let mut printed_paths = FxHashSet::default(); + prints.extend(matches.opt_strs("print").into_iter().map(|req| { let (req, out) = split_out_file_name(&req); @@ -2165,6 +2171,15 @@ fn collect_print_requests( }; let out = out.unwrap_or(OutFileName::Stdout); + if let OutFileName::Real(path) = &out { + if !printed_paths.insert(path.clone()) { + handler.early_error(format!( + "cannot print multiple outputs to the same path: {}", + path.display(), + )); + } + } + PrintRequest { kind, out } })); From f2e3d3fc63ea238385aecbb77f633fd451b9470a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 16 Jul 2023 22:13:08 -0700 Subject: [PATCH 057/113] Move OutFileName writing into rustc_session --- compiler/rustc_driver_impl/messages.ftl | 2 -- compiler/rustc_driver_impl/src/pretty.rs | 13 +------------ .../rustc_driver_impl/src/session_diagnostics.rs | 7 ------- compiler/rustc_session/messages.ftl | 2 ++ compiler/rustc_session/src/config.rs | 13 +++++++++++++ compiler/rustc_session/src/errors.rs | 7 +++++++ tests/ui/unpretty/avoid-crash.stderr | 2 +- 7 files changed, 24 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl index 22b4ec6b0d1b..f8e25e008008 100644 --- a/compiler/rustc_driver_impl/messages.ftl +++ b/compiler/rustc_driver_impl/messages.ftl @@ -15,5 +15,3 @@ driver_impl_rlink_rustc_version_mismatch = .rlink file was produced by rustc ver driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}` driver_impl_rlink_wrong_file_type = The input does not look like a .rlink file - -driver_impl_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}` diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 24a5f4030b88..222c7b5d6a72 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -1,6 +1,5 @@ //! The various pretty-printing routines. -use crate::session_diagnostics::UnprettyDumpFail; use rustc_ast as ast; use rustc_ast_pretty::pprust; use rustc_errors::ErrorGuaranteed; @@ -358,17 +357,7 @@ fn get_source(sess: &Session) -> (String, FileName) { } fn write_or_print(out: &str, sess: &Session) { - match &sess.io.output_file { - None | Some(OutFileName::Stdout) => print!("{out}"), - Some(OutFileName::Real(p)) => { - if let Err(e) = std::fs::write(p, out) { - sess.emit_fatal(UnprettyDumpFail { - path: p.display().to_string(), - err: e.to_string(), - }); - } - } - } + sess.io.output_file.as_ref().unwrap_or(&OutFileName::Stdout).overwrite(out, sess); } pub fn print_after_parsing(sess: &Session, krate: &ast::Crate, ppm: PpMode) { diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index 638b368f7021..8e5347eba96c 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -32,13 +32,6 @@ pub(crate) struct RLinkRustcVersionMismatch<'a> { #[diag(driver_impl_rlink_no_a_file)] pub(crate) struct RlinkNotAFile; -#[derive(Diagnostic)] -#[diag(driver_impl_unpretty_dump_fail)] -pub(crate) struct UnprettyDumpFail { - pub path: String, - pub err: String, -} - #[derive(Diagnostic)] #[diag(driver_impl_ice)] pub(crate) struct Ice; diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 4897bd8d5dae..ee24c6d902fa 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -26,6 +26,8 @@ session_feature_gate_error = {$explain} session_file_is_not_writeable = output file {$file} is not writeable -- check its permissions +session_file_write_fail = failed to write `{$path}` due to error `{$err}` + session_hexadecimal_float_literal_not_supported = hexadecimal float literal is not supported session_incompatible_linker_flavor = linker flavor `{$flavor}` is incompatible with the current target diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index ee871dc8005c..a8147ede970c 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3,6 +3,7 @@ pub use crate::options::*; +use crate::errors::FileWriteFail; use crate::search_paths::SearchPath; use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; use crate::{lint, HashStableContext}; @@ -31,6 +32,7 @@ use std::collections::btree_map::{ use std::collections::{BTreeMap, BTreeSet}; use std::ffi::OsStr; use std::fmt; +use std::fs; use std::hash::Hash; use std::iter; use std::path::{Path, PathBuf}; @@ -861,6 +863,17 @@ impl OutFileName { OutFileName::Stdout => outputs.temp_path(flavor, codegen_unit_name), } } + + pub fn overwrite(&self, content: &str, sess: &Session) { + match self { + OutFileName::Stdout => print!("{content}"), + OutFileName::Real(path) => { + if let Err(e) = fs::write(path, content) { + sess.emit_fatal(FileWriteFail { path, err: e.to_string() }); + } + } + } + } } #[derive(Clone, Hash, Debug, HashStable_Generic)] diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 4a3e668da111..dd15ad45145f 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -163,6 +163,13 @@ pub struct FileIsNotWriteable<'a> { pub file: &'a std::path::Path, } +#[derive(Diagnostic)] +#[diag(session_file_write_fail)] +pub(crate) struct FileWriteFail<'a> { + pub path: &'a std::path::Path, + pub err: String, +} + #[derive(Diagnostic)] #[diag(session_crate_name_does_not_match)] pub struct CrateNameDoesNotMatch { diff --git a/tests/ui/unpretty/avoid-crash.stderr b/tests/ui/unpretty/avoid-crash.stderr index 11cd3866fa86..15bcc277e649 100644 --- a/tests/ui/unpretty/avoid-crash.stderr +++ b/tests/ui/unpretty/avoid-crash.stderr @@ -1,4 +1,4 @@ -error: pretty-print failed to write `/tmp/` due to $ERROR_MESSAGE +error: failed to write `/tmp/` due to $ERROR_MESSAGE error: aborting due to previous error From 5a60660ff8c35e2f270658900073443bbb984180 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 16 Jul 2023 22:29:05 -0700 Subject: [PATCH 058/113] Implement printing to file in print_crate_info --- compiler/rustc_driver_impl/src/lib.rs | 55 ++++++++++++++++++--------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 27dfb6c3bca6..a17310206323 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -49,6 +49,7 @@ use std::cmp::max; use std::collections::BTreeMap; use std::env; use std::ffi::OsString; +use std::fmt::Write as _; use std::fs; use std::io::{self, IsTerminal, Read, Write}; use std::panic::{self, catch_unwind}; @@ -65,6 +66,11 @@ macro do_not_use_print($($t:tt)*) { ) } +#[allow(unused_macros)] +macro do_not_use_safe_print($($t:tt)*) { + std::compile_error!("Don't use `safe_print` or `safe_println` here, use `println_info` instead") +} + // This import blocks the use of panicking `print` and `println` in all the code // below. Please use `safe_print` and `safe_println` to avoid ICE when // encountering an I/O error during print. @@ -713,6 +719,13 @@ fn print_crate_info( parse_attrs: bool, ) -> Compilation { use rustc_session::config::PrintKind::*; + + // This import prevents the following code from using the printing macros + // used by the rest of the module. Within this function, we only write to + // the output specified by `sess.io.output_file`. + #[allow(unused_imports)] + use {do_not_use_safe_print as safe_print, do_not_use_safe_print as safe_println}; + // NativeStaticLibs and LinkArgs are special - printed during linking // (empty iterator returns true) if sess.opts.prints.iter().all(|p| p.kind == NativeStaticLibs || p.kind == LinkArgs) { @@ -731,17 +744,23 @@ fn print_crate_info( } else { None }; + for req in &sess.opts.prints { + let mut crate_info = String::new(); + macro println_info($($arg:tt)*) { + crate_info.write_fmt(format_args!("{}\n", format_args!($($arg)*))).unwrap() + } + match req.kind { TargetList => { let mut targets = rustc_target::spec::TARGETS.to_vec(); targets.sort_unstable(); - safe_println!("{}", targets.join("\n")); + println_info!("{}", targets.join("\n")); } - Sysroot => safe_println!("{}", sess.sysroot.display()), - TargetLibdir => safe_println!("{}", sess.target_tlib_path.dir.display()), + Sysroot => println_info!("{}", sess.sysroot.display()), + TargetLibdir => println_info!("{}", sess.target_tlib_path.dir.display()), TargetSpec => { - safe_println!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap()); + println_info!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap()); } AllTargetSpecs => { let mut targets = BTreeMap::new(); @@ -750,7 +769,7 @@ fn print_crate_info( let target = Target::expect_builtin(&triple); targets.insert(name, target.to_json()); } - safe_println!("{}", serde_json::to_string_pretty(&targets).unwrap()); + println_info!("{}", serde_json::to_string_pretty(&targets).unwrap()); } FileNames | CrateName => { let Some(attrs) = attrs.as_ref() else { @@ -760,14 +779,14 @@ fn print_crate_info( let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess); let id = rustc_session::output::find_crate_name(sess, attrs); if req.kind == CrateName { - safe_println!("{id}"); - continue; - } - let crate_types = collect_crate_types(sess, attrs); - for &style in &crate_types { - let fname = - rustc_session::output::filename_for_input(sess, style, id, &t_outputs); - safe_println!("{}", fname.as_path().file_name().unwrap().to_string_lossy()); + println_info!("{id}"); + } else { + let crate_types = collect_crate_types(sess, attrs); + for &style in &crate_types { + let fname = + rustc_session::output::filename_for_input(sess, style, id, &t_outputs); + println_info!("{}", fname.as_path().file_name().unwrap().to_string_lossy()); + } } } Cfg => { @@ -801,13 +820,13 @@ fn print_crate_info( cfgs.sort(); for cfg in cfgs { - safe_println!("{cfg}"); + println_info!("{cfg}"); } } CallingConventions => { let mut calling_conventions = rustc_target::spec::abi::all_names(); calling_conventions.sort_unstable(); - safe_println!("{}", calling_conventions.join("\n")); + println_info!("{}", calling_conventions.join("\n")); } RelocationModels | CodeModels @@ -825,7 +844,7 @@ fn print_crate_info( for split in &[Off, Packed, Unpacked] { if sess.target.options.supported_split_debuginfo.contains(split) { - safe_println!("{split}"); + println_info!("{split}"); } } } @@ -833,7 +852,7 @@ fn print_crate_info( use rustc_target::spec::current_apple_deployment_target; if sess.target.is_like_osx { - safe_println!( + println_info!( "deployment_target={}", current_apple_deployment_target(&sess.target) .expect("unknown Apple target OS") @@ -844,6 +863,8 @@ fn print_crate_info( } } } + + req.out.overwrite(&crate_info, sess); } Compilation::Stop } From c80cbe4baedfe1ef8ea6f88f3cf2f8db06c8c399 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 16 Jul 2023 22:33:38 -0700 Subject: [PATCH 059/113] Implement printing to file in codegen_backend.print --- compiler/rustc_codegen_llvm/src/lib.rs | 23 ++++++++++--------- .../rustc_codegen_ssa/src/traits/backend.rs | 20 +++++++++++++++- compiler/rustc_codegen_ssa/src/traits/mod.rs | 2 +- compiler/rustc_driver_impl/src/lib.rs | 2 +- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 38a32ac845d2..3ff664136470 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -262,10 +262,10 @@ impl CodegenBackend for LlvmCodegenBackend { |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true) } - fn print(&self, req: &PrintRequest, sess: &Session) { + fn print(&self, req: &PrintRequest, out: &mut dyn PrintBackendInfo, sess: &Session) { match req.kind { PrintKind::RelocationModels => { - println!("Available relocation models:"); + writeln!(out, "Available relocation models:"); for name in &[ "static", "pic", @@ -276,26 +276,27 @@ impl CodegenBackend for LlvmCodegenBackend { "ropi-rwpi", "default", ] { - println!(" {}", name); + writeln!(out, " {}", name); } - println!(); + writeln!(out); } PrintKind::CodeModels => { - println!("Available code models:"); + writeln!(out, "Available code models:"); for name in &["tiny", "small", "kernel", "medium", "large"] { - println!(" {}", name); + writeln!(out, " {}", name); } - println!(); + writeln!(out); } PrintKind::TlsModels => { - println!("Available TLS models:"); + writeln!(out, "Available TLS models:"); for name in &["global-dynamic", "local-dynamic", "initial-exec", "local-exec"] { - println!(" {}", name); + writeln!(out, " {}", name); } - println!(); + writeln!(out); } PrintKind::StackProtectorStrategies => { - println!( + writeln!( + out, r#"Available stack protector strategies: all Generate stack canaries in all functions. diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index f10bbbeb97cc..1991b55f1914 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -23,6 +23,8 @@ use rustc_span::symbol::Symbol; use rustc_target::abi::call::FnAbi; use rustc_target::spec::Target; +use std::fmt; + pub trait BackendTypes { type Value: CodegenObject; type Function: CodegenObject; @@ -61,7 +63,7 @@ pub trait CodegenBackend { fn locale_resource(&self) -> &'static str; fn init(&self, _sess: &Session) {} - fn print(&self, _req: &PrintRequest, _sess: &Session) {} + fn print(&self, _req: &PrintRequest, _out: &mut dyn PrintBackendInfo, _sess: &Session) {} fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec { vec![] } @@ -162,3 +164,19 @@ pub trait ExtraBackendMethods: std::thread::Builder::new().name(name).spawn(f) } } + +pub trait PrintBackendInfo { + fn infallible_write_fmt(&mut self, args: fmt::Arguments<'_>); +} + +impl PrintBackendInfo for String { + fn infallible_write_fmt(&mut self, args: fmt::Arguments<'_>) { + fmt::Write::write_fmt(self, args).unwrap(); + } +} + +impl dyn PrintBackendInfo + '_ { + pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) { + self.infallible_write_fmt(args); + } +} diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 8cb58bd4c704..0b69df33d264 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -30,7 +30,7 @@ mod write; pub use self::abi::AbiBuilderMethods; pub use self::asm::{AsmBuilderMethods, AsmMethods, GlobalAsmOperandRef, InlineAsmOperandRef}; -pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods}; +pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods, PrintBackendInfo}; pub use self::builder::{BuilderMethods, OverflowOp}; pub use self::consts::ConstMethods; pub use self::coverageinfo::CoverageInfoBuilderMethods; diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index a17310206323..9e1d7499a352 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -834,7 +834,7 @@ fn print_crate_info( | TargetCPUs | StackProtectorStrategies | TargetFeatures => { - codegen_backend.print(req, sess); + codegen_backend.print(req, &mut crate_info, sess); } // Any output here interferes with Cargo's parsing of other printed output NativeStaticLibs => {} From 6e734fce633fa5de1140f8e5d1dc981c40be1f92 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 13 Jul 2023 16:54:25 -0700 Subject: [PATCH 060/113] Implement printing to file in llvm_util --- compiler/rustc_codegen_llvm/src/lib.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm_util.rs | 25 ++++++++++---------- compiler/rustc_codegen_ssa/src/traits/mod.rs | 4 +++- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 3ff664136470..50cc44bfbb45 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -320,7 +320,7 @@ impl CodegenBackend for LlvmCodegenBackend { "# ); } - _other => llvm_util::print(req, sess), + _other => llvm_util::print(req, out, sess), } } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 84d9acf45de5..844eb58333fc 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -8,6 +8,7 @@ use libc::c_int; use rustc_codegen_ssa::target_features::{ supported_target_features, tied_target_features, RUSTC_SPECIFIC_FEATURES, }; +use rustc_codegen_ssa::traits::PrintBackendInfo; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::small_c_str::SmallCStr; use rustc_fs_util::path_to_c_string; @@ -350,7 +351,7 @@ fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> { ret } -fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) { +fn print_target_features(out: &mut dyn PrintBackendInfo, sess: &Session, tm: &llvm::TargetMachine) { let mut llvm_target_features = llvm_target_features(tm); let mut known_llvm_target_features = FxHashSet::<&'static str>::default(); let mut rustc_target_features = supported_target_features(sess) @@ -383,24 +384,24 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) { .max() .unwrap_or(0); - println!("Features supported by rustc for this target:"); + writeln!(out, "Features supported by rustc for this target:"); for (feature, desc) in &rustc_target_features { - println!(" {1:0$} - {2}.", max_feature_len, feature, desc); + writeln!(out, " {1:0$} - {2}.", max_feature_len, feature, desc); } - println!("\nCode-generation features supported by LLVM for this target:"); + writeln!(out, "\nCode-generation features supported by LLVM for this target:"); for (feature, desc) in &llvm_target_features { - println!(" {1:0$} - {2}.", max_feature_len, feature, desc); + writeln!(out, " {1:0$} - {2}.", max_feature_len, feature, desc); } if llvm_target_features.is_empty() { - println!(" Target features listing is not supported by this LLVM version."); + writeln!(out, " Target features listing is not supported by this LLVM version."); } - println!("\nUse +feature to enable a feature, or -feature to disable it."); - println!("For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n"); - println!("Code-generation features cannot be used in cfg or #[target_feature],"); - println!("and may be renamed or removed in a future version of LLVM or rustc.\n"); + writeln!(out, "\nUse +feature to enable a feature, or -feature to disable it."); + writeln!(out, "For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n"); + writeln!(out, "Code-generation features cannot be used in cfg or #[target_feature],"); + writeln!(out, "and may be renamed or removed in a future version of LLVM or rustc.\n"); } -pub(crate) fn print(req: &PrintRequest, sess: &Session) { +pub(crate) fn print(req: &PrintRequest, out: &mut dyn PrintBackendInfo, sess: &Session) { require_inited(); let tm = create_informational_target_machine(sess); match req.kind { @@ -412,7 +413,7 @@ pub(crate) fn print(req: &PrintRequest, sess: &Session) { .unwrap_or_else(|e| bug!("failed to convert to cstring: {}", e)); unsafe { llvm::LLVMRustPrintTargetCPUs(tm, cpu_cstring.as_ptr()) }; } - PrintKind::TargetFeatures => print_target_features(sess, tm), + PrintKind::TargetFeatures => print_target_features(out, sess, tm), _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), } } diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 0b69df33d264..728c2bc8c49b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -30,7 +30,9 @@ mod write; pub use self::abi::AbiBuilderMethods; pub use self::asm::{AsmBuilderMethods, AsmMethods, GlobalAsmOperandRef, InlineAsmOperandRef}; -pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods, PrintBackendInfo}; +pub use self::backend::{ + Backend, BackendTypes, CodegenBackend, ExtraBackendMethods, PrintBackendInfo, +}; pub use self::builder::{BuilderMethods, OverflowOp}; pub use self::consts::ConstMethods; pub use self::coverageinfo::CoverageInfoBuilderMethods; From 815a114974debd5b9c6a19a8d94f96b675a27347 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 13 Jul 2023 16:56:29 -0700 Subject: [PATCH 061/113] Implement printing to file in PassWrapper --- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 7 +++- compiler/rustc_codegen_llvm/src/llvm_util.rs | 18 +++++++++-- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 32 +++++++++++++------ 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 605f0154773a..03b5b45d9a3d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2280,7 +2280,12 @@ extern "C" { pub fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool; - pub fn LLVMRustPrintTargetCPUs(T: &TargetMachine, cpu: *const c_char); + pub fn LLVMRustPrintTargetCPUs( + T: &TargetMachine, + cpu: *const c_char, + print: unsafe extern "C" fn(out: *mut c_void, string: *const c_char, len: usize), + out: *mut c_void, + ); pub fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t; pub fn LLVMRustGetTargetFeature( T: &TargetMachine, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 844eb58333fc..8d57f59764fb 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -17,8 +17,8 @@ use rustc_session::config::{PrintKind, PrintRequest}; use rustc_session::Session; use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy}; -use std::ffi::{CStr, CString}; +use std::ffi::{c_char, c_void, CStr, CString}; use std::path::Path; use std::ptr; use std::slice; @@ -401,7 +401,7 @@ fn print_target_features(out: &mut dyn PrintBackendInfo, sess: &Session, tm: &ll writeln!(out, "and may be renamed or removed in a future version of LLVM or rustc.\n"); } -pub(crate) fn print(req: &PrintRequest, out: &mut dyn PrintBackendInfo, sess: &Session) { +pub(crate) fn print(req: &PrintRequest, mut out: &mut dyn PrintBackendInfo, sess: &Session) { require_inited(); let tm = create_informational_target_machine(sess); match req.kind { @@ -411,7 +411,19 @@ pub(crate) fn print(req: &PrintRequest, out: &mut dyn PrintBackendInfo, sess: &S // at least as long as the C function let cpu_cstring = CString::new(handle_native(sess.target.cpu.as_ref())) .unwrap_or_else(|e| bug!("failed to convert to cstring: {}", e)); - unsafe { llvm::LLVMRustPrintTargetCPUs(tm, cpu_cstring.as_ptr()) }; + unsafe extern "C" fn callback(out: *mut c_void, string: *const c_char, len: usize) { + let out = &mut *(out as *mut &mut dyn PrintBackendInfo); + let bytes = slice::from_raw_parts(string as *const u8, len); + write!(out, "{}", String::from_utf8_lossy(bytes)); + } + unsafe { + llvm::LLVMRustPrintTargetCPUs( + tm, + cpu_cstring.as_ptr(), + callback, + &mut out as *mut &mut dyn PrintBackendInfo as *mut c_void, + ); + } } PrintKind::TargetFeatures => print_target_features(out, sess, tm), _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index eb3d67e720f2..e5fb6b0953f5 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -306,44 +307,55 @@ static size_t getLongestEntryLength(ArrayRef Table) { return MaxLen; } -extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, const char* TargetCPU) { +using PrintBackendInfo = void(void*, const char* Data, size_t Len); + +extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, + const char* TargetCPU, + PrintBackendInfo Print, + void* Out) { const TargetMachine *Target = unwrap(TM); const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); const Triple::ArchType HostArch = Triple(sys::getDefaultTargetTriple()).getArch(); const Triple::ArchType TargetArch = Target->getTargetTriple().getArch(); + std::ostringstream Buf; + #if LLVM_VERSION_GE(17, 0) const ArrayRef CPUTable = MCInfo->getAllProcessorDescriptions(); #elif defined(LLVM_RUSTLLVM) const ArrayRef CPUTable = MCInfo->getCPUTable(); #else - printf("Full target CPU help is not supported by this LLVM version.\n\n"); + Buf << "Full target CPU help is not supported by this LLVM version.\n\n"; SubtargetSubTypeKV TargetCPUKV = { TargetCPU, {{}}, {{}} }; const ArrayRef CPUTable = TargetCPUKV; #endif unsigned MaxCPULen = getLongestEntryLength(CPUTable); - printf("Available CPUs for this target:\n"); + Buf << "Available CPUs for this target:\n"; // Don't print the "native" entry when the user specifies --target with a // different arch since that could be wrong or misleading. if (HostArch == TargetArch) { MaxCPULen = std::max(MaxCPULen, (unsigned) std::strlen("native")); const StringRef HostCPU = sys::getHostCPUName(); - printf(" %-*s - Select the CPU of the current host (currently %.*s).\n", - MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data()); + Buf << " " << std::left << std::setw(MaxCPULen) << "native" + << " - Select the CPU of the current host " + "(currently " << HostCPU.str() << ").\n"; } for (auto &CPU : CPUTable) { // Compare cpu against current target to label the default if (strcmp(CPU.Key, TargetCPU) == 0) { - printf(" %-*s - This is the default target CPU" - " for the current build target (currently %s).", - MaxCPULen, CPU.Key, Target->getTargetTriple().str().c_str()); + Buf << " " << std::left << std::setw(MaxCPULen) << CPU.Key + << " - This is the default target CPU for the current build target " + "(currently " << Target->getTargetTriple().str() << ")."; } else { - printf(" %-*s", MaxCPULen, CPU.Key); + Buf << " " << CPU.Key; } - printf("\n"); + Buf << "\n"; } + + const auto &BufString = Buf.str(); + Print(Out, BufString.data(), BufString.size()); } extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) { From dcfe94a009369734c9c84930f42afc1f6d17998d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 16 Jul 2023 23:08:04 -0700 Subject: [PATCH 062/113] Implement printing to file for link-args and native-static-libs --- compiler/rustc_codegen_ssa/src/back/link.rs | 32 ++++++++++++++------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index bb8a520321a1..ecd58541e992 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -12,7 +12,7 @@ use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME}; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; -use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, Strip}; +use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, OutFileName, Strip}; use rustc_session::config::{OutputFilenames, OutputType, PrintKind, SplitDwarfKind}; use rustc_session::cstore::DllImport; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; @@ -596,8 +596,10 @@ fn link_staticlib<'a>( all_native_libs.extend_from_slice(&codegen_results.crate_info.used_libraries); - if sess.opts.prints.iter().any(|print| print.kind == PrintKind::NativeStaticLibs) { - print_native_static_libs(sess, &all_native_libs, &all_rust_dylibs); + for print in &sess.opts.prints { + if print.kind == PrintKind::NativeStaticLibs { + print_native_static_libs(sess, &print.out, &all_native_libs, &all_rust_dylibs); + } } Ok(()) @@ -744,8 +746,11 @@ fn link_natively<'a>( cmd.env_remove(k.as_ref()); } - if sess.opts.prints.iter().any(|print| print.kind == PrintKind::LinkArgs) { - println!("{:?}", &cmd); + for print in &sess.opts.prints { + if print.kind == PrintKind::LinkArgs { + let content = format!("{:?}", cmd); + print.out.overwrite(&content, sess); + } } // May have not found libraries in the right formats. @@ -1386,6 +1391,7 @@ enum RlibFlavor { fn print_native_static_libs( sess: &Session, + out: &OutFileName, all_native_libs: &[NativeLib], all_rust_dylibs: &[&Path], ) { @@ -1459,11 +1465,17 @@ fn print_native_static_libs( lib_args.push(format!("-l{}", lib)); } } - if !lib_args.is_empty() { - sess.emit_note(errors::StaticLibraryNativeArtifacts); - // Prefix for greppability - // Note: This must not be translated as tools are allowed to depend on this exact string. - sess.note_without_error(format!("native-static-libs: {}", &lib_args.join(" "))); + + match out { + OutFileName::Real(_) => out.overwrite(&lib_args.join(" "), sess), + OutFileName::Stdout => { + if !lib_args.is_empty() { + sess.emit_note(errors::StaticLibraryNativeArtifacts); + // Prefix for greppability + // Note: This must not be translated as tools are allowed to depend on this exact string. + sess.note_without_error(format!("native-static-libs: {}", &lib_args.join(" "))); + } + } } } From 7ee059b8ac986f5c127063870336e934f898530e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 14 Jul 2023 10:51:54 -0700 Subject: [PATCH 063/113] Add ui test of LLVM print-from-C++ changes --- tests/ui/codegen/target-cpus.rs | 4 ++++ tests/ui/codegen/target-cpus.stdout | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 tests/ui/codegen/target-cpus.rs create mode 100644 tests/ui/codegen/target-cpus.stdout diff --git a/tests/ui/codegen/target-cpus.rs b/tests/ui/codegen/target-cpus.rs new file mode 100644 index 000000000000..1dff3ee6011b --- /dev/null +++ b/tests/ui/codegen/target-cpus.rs @@ -0,0 +1,4 @@ +// needs-llvm-components: webassembly +// min-llvm-version: 17 +// compile-flags: --print=target-cpus --target=wasm32-unknown-unknown +// check-pass diff --git a/tests/ui/codegen/target-cpus.stdout b/tests/ui/codegen/target-cpus.stdout new file mode 100644 index 000000000000..f60ba0f5034b --- /dev/null +++ b/tests/ui/codegen/target-cpus.stdout @@ -0,0 +1,4 @@ +Available CPUs for this target: + bleeding-edge + generic - This is the default target CPU for the current build target (currently wasm32-unknown-unknown). + mvp From 5ca0946ac0ae7c75feb3474039c7fb9a5348601d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 16 Jul 2023 23:05:12 -0700 Subject: [PATCH 064/113] Document --print KIND=PATH in Command-line Arguments documentation --- src/doc/rustc/src/command-line-arguments.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 2c7c05c0c4b8..4d32897cc14c 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -260,6 +260,10 @@ The valid types of print values are: This returns rustc's minimum supported deployment target if no `*_DEPLOYMENT_TARGET` variable is present in the environment, or otherwise returns the variable's parsed value. +A filepath may optionally be specified for each requested information kind, in +the format `--print KIND=PATH`, just like for `--emit`. When a path is +specified, information will be written there instead of to stdout. + [conditional compilation]: ../reference/conditional-compilation.html [deployment target]: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/cross_development/Configuring/configuring.html From 26fd6b15b058df4b53fd95109a92316a74e7e94a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 19 Jul 2023 15:13:40 -0700 Subject: [PATCH 065/113] Add note about writing native-static-libs to file --- compiler/rustc_codegen_ssa/messages.ftl | 2 ++ compiler/rustc_codegen_ssa/src/back/link.rs | 7 ++++++- compiler/rustc_codegen_ssa/src/errors.rs | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index f73080182bfc..b6c70c622497 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -197,6 +197,8 @@ codegen_ssa_specify_libraries_to_link = use the `-l` flag to specify native libr codegen_ssa_static_library_native_artifacts = Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms. +codegen_ssa_static_library_native_artifacts_to_file = Native artifacts to link against have been written to {$path}. The order and any duplication can be significant on some platforms. + codegen_ssa_stripping_debug_info_failed = stripping debug info with `{$util}` failed: {$status} .note = {$output} diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index ecd58541e992..eefa4ac34dde 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1467,7 +1467,12 @@ fn print_native_static_libs( } match out { - OutFileName::Real(_) => out.overwrite(&lib_args.join(" "), sess), + OutFileName::Real(path) => { + out.overwrite(&lib_args.join(" "), sess); + if !lib_args.is_empty() { + sess.emit_note(errors::StaticLibraryNativeArtifactsToFile { path }); + } + } OutFileName::Stdout => { if !lib_args.is_empty() { sess.emit_note(errors::StaticLibraryNativeArtifacts); diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 056b4abd2353..c72d37be7481 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -455,6 +455,12 @@ pub struct LinkerFileStem; #[diag(codegen_ssa_static_library_native_artifacts)] pub struct StaticLibraryNativeArtifacts; +#[derive(Diagnostic)] +#[diag(codegen_ssa_static_library_native_artifacts_to_file)] +pub struct StaticLibraryNativeArtifactsToFile<'a> { + pub path: &'a Path, +} + #[derive(Diagnostic)] #[diag(codegen_ssa_link_script_unavailable)] pub struct LinkScriptUnavailable; From 11ae0afc931b00f921818eb99dd894768558bdcf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 19 Jul 2023 15:15:46 -0700 Subject: [PATCH 066/113] Create separate match arms for FileNames and CrateNames This introduces a bit of code duplication, but we don't have the build_output_filenames in the CrateName arm and this seems a little cleaner overall. --- compiler/rustc_driver_impl/src/lib.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 9e1d7499a352..f2286fef370a 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -771,24 +771,28 @@ fn print_crate_info( } println_info!("{}", serde_json::to_string_pretty(&targets).unwrap()); } - FileNames | CrateName => { + FileNames => { let Some(attrs) = attrs.as_ref() else { // no crate attributes, print out an error and exit return Compilation::Continue; }; let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess); let id = rustc_session::output::find_crate_name(sess, attrs); - if req.kind == CrateName { - println_info!("{id}"); - } else { - let crate_types = collect_crate_types(sess, attrs); - for &style in &crate_types { - let fname = - rustc_session::output::filename_for_input(sess, style, id, &t_outputs); - println_info!("{}", fname.as_path().file_name().unwrap().to_string_lossy()); - } + let crate_types = collect_crate_types(sess, attrs); + for &style in &crate_types { + let fname = + rustc_session::output::filename_for_input(sess, style, id, &t_outputs); + println_info!("{}", fname.as_path().file_name().unwrap().to_string_lossy()); } } + CrateName => { + let Some(attrs) = attrs.as_ref() else { + // no crate attributes, print out an error and exit + return Compilation::Continue; + }; + let id = rustc_session::output::find_crate_name(sess, attrs); + println_info!("{id}"); + } Cfg => { let mut cfgs = sess .parse_sess From 34732e8560a3fea72bab9f2c9840897faa4f55b5 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Tue, 4 Jul 2023 16:01:16 -0700 Subject: [PATCH 067/113] Get `!nonnull` metadata consistently in slice iterators, without needing `assume`s --- library/core/src/ptr/non_null.rs | 24 +++ library/core/src/slice/iter.rs | 25 +-- library/core/src/slice/iter/macros.rs | 146 ++++++++++-------- tests/codegen/iter-repeat-n-trivial-drop.rs | 1 + tests/codegen/slice-iter-len-eq-zero.rs | 4 +- tests/codegen/slice-iter-nonnull.rs | 39 +++++ ...ated_loop.PreCodegen.after.panic-abort.mir | 4 +- ...ted_loop.PreCodegen.after.panic-unwind.mir | 4 +- ...ward_loop.PreCodegen.after.panic-abort.mir | 4 +- ...ard_loop.PreCodegen.after.panic-unwind.mir | 4 +- ...erse_loop.PreCodegen.after.panic-abort.mir | 4 +- ...rse_loop.PreCodegen.after.panic-unwind.mir | 4 +- 12 files changed, 170 insertions(+), 93 deletions(-) diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index b492d2f07bc1..a8074c8659bd 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -462,6 +462,30 @@ impl NonNull { // And the caller promised the `delta` is sound to add. unsafe { NonNull { pointer: self.pointer.add(delta) } } } + + /// See [`pointer::sub`] for semantics and safety requirements. + #[inline] + pub(crate) const unsafe fn sub(self, delta: usize) -> Self + where + T: Sized, + { + // SAFETY: We require that the delta stays in-bounds of the object, and + // thus it cannot become null, as no legal objects can be allocated + // in such as way that the null address is part of them. + // And the caller promised the `delta` is sound to subtract. + unsafe { NonNull { pointer: self.pointer.sub(delta) } } + } + + /// See [`pointer::sub_ptr`] for semantics and safety requirements. + #[inline] + pub(crate) const unsafe fn sub_ptr(self, subtrahend: Self) -> usize + where + T: Sized, + { + // SAFETY: The caller promised that this is safe to do, and + // the non-nullness is irrelevant to the operation. + unsafe { self.pointer.sub_ptr(subtrahend.pointer) } + } } impl NonNull<[T]> { diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 5369fe0a9a9a..cc9313553183 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -13,7 +13,7 @@ use crate::iter::{ use crate::marker::{PhantomData, Send, Sized, Sync}; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZeroUsize; -use crate::ptr::{invalid, invalid_mut, NonNull}; +use crate::ptr::{self, invalid, invalid_mut, NonNull}; use super::{from_raw_parts, from_raw_parts_mut}; @@ -68,7 +68,7 @@ pub struct Iter<'a, T: 'a> { /// For non-ZSTs, the non-null pointer to the past-the-end element. /// /// For ZSTs, this is `ptr::invalid(len)`. - end: *const T, + end_or_len: *const T, _marker: PhantomData<&'a T>, } @@ -90,9 +90,9 @@ impl<'a, T> Iter<'a, T> { let ptr = slice.as_ptr(); // SAFETY: Similar to `IterMut::new`. unsafe { - let end = if T::IS_ZST { invalid(slice.len()) } else { ptr.add(slice.len()) }; + let end_or_len = if T::IS_ZST { invalid(slice.len()) } else { ptr.add(slice.len()) }; - Self { ptr: NonNull::new_unchecked(ptr as *mut T), end, _marker: PhantomData } + Self { ptr: NonNull::new_unchecked(ptr as *mut T), end_or_len, _marker: PhantomData } } } @@ -128,7 +128,7 @@ impl<'a, T> Iter<'a, T> { } } -iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, { +iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, as_ref, { fn is_sorted_by(self, mut compare: F) -> bool where Self: Sized, @@ -142,7 +142,7 @@ iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, { impl Clone for Iter<'_, T> { #[inline] fn clone(&self) -> Self { - Iter { ptr: self.ptr, end: self.end, _marker: self._marker } + Iter { ptr: self.ptr, end_or_len: self.end_or_len, _marker: self._marker } } } @@ -189,7 +189,7 @@ pub struct IterMut<'a, T: 'a> { /// For non-ZSTs, the non-null pointer to the past-the-end element. /// /// For ZSTs, this is `ptr::invalid_mut(len)`. - end: *mut T, + end_or_len: *mut T, _marker: PhantomData<&'a mut T>, } @@ -220,15 +220,16 @@ impl<'a, T> IterMut<'a, T> { // for direct pointer equality with `ptr` to check if the iterator is // done. // - // In the case of a ZST, the end pointer is just the start pointer plus - // the length, to also allows for the fast `ptr == end` check. + // In the case of a ZST, the end pointer is just the length. It's never + // used as a pointer at all, and thus it's fine to have no provenance. // // See the `next_unchecked!` and `is_empty!` macros as well as the // `post_inc_start` method for more information. unsafe { - let end = if T::IS_ZST { invalid_mut(slice.len()) } else { ptr.add(slice.len()) }; + let end_or_len = + if T::IS_ZST { invalid_mut(slice.len()) } else { ptr.add(slice.len()) }; - Self { ptr: NonNull::new_unchecked(ptr), end, _marker: PhantomData } + Self { ptr: NonNull::new_unchecked(ptr), end_or_len, _marker: PhantomData } } } @@ -360,7 +361,7 @@ impl AsRef<[T]> for IterMut<'_, T> { // } // } -iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, {}} +iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, as_mut, {}} /// An internal abstraction over the splitting iterators, so that /// splitn, splitn_mut etc can be implemented once. diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index 96a145e22ed5..95bcd123b824 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -1,45 +1,62 @@ //! Macros used by iterators of slice. -// Shrinks the iterator when T is a ZST, setting the length to `new_len`. -// `new_len` must not exceed `self.len()`. -macro_rules! zst_set_len { - ($self: ident, $new_len: expr) => {{ +/// Convenience & performance macro for consuming the `end_or_len` field, by +/// giving a `(&mut) usize` or `(&mut) NonNull` depending whether `T` is +/// or is not a ZST respectively. +/// +/// Internally, this reads the `end` through a pointer-to-`NonNull` so that +/// it'll get the appropriate non-null metadata in the backend without needing +/// to call `assume` manually. +macro_rules! if_zst { + (mut $this:ident, $len:ident => $zst_body:expr, $end:ident => $other_body:expr,) => {{ #![allow(unused_unsafe)] // we're sometimes used within an unsafe block - // SAFETY: same as `invalid(_mut)`, but the macro doesn't know - // which versions of that function to call, so open-code it. - $self.end = unsafe { mem::transmute::($new_len) }; + if T::IS_ZST { + // SAFETY: for ZSTs, the pointer is storing a provenance-free length, + // so consuming and updating it as a `usize` is fine. + let $len = unsafe { &mut *ptr::addr_of_mut!($this.end_or_len).cast::() }; + $zst_body + } else { + // SAFETY: for non-ZSTs, the type invariant ensures it cannot be null + let $end = unsafe { &mut *ptr::addr_of_mut!($this.end_or_len).cast::>() }; + $other_body + } }}; -} + ($this:ident, $len:ident => $zst_body:expr, $end:ident => $other_body:expr,) => {{ + #![allow(unused_unsafe)] // we're sometimes used within an unsafe block -// Shrinks the iterator when T is a ZST, reducing the length by `n`. -// `n` must not exceed `self.len()`. -macro_rules! zst_shrink { - ($self: ident, $n: ident) => { - let new_len = $self.end.addr() - $n; - zst_set_len!($self, new_len); - }; + if T::IS_ZST { + let $len = $this.end_or_len.addr(); + $zst_body + } else { + // SAFETY: for non-ZSTs, the type invariant ensures it cannot be null + let $end = unsafe { *ptr::addr_of!($this.end_or_len).cast::>() }; + $other_body + } + }}; } // Inlining is_empty and len makes a huge performance difference macro_rules! is_empty { ($self: ident) => { - if T::IS_ZST { $self.end.addr() == 0 } else { $self.ptr.as_ptr() as *const _ == $self.end } + if_zst!($self, + len => len == 0, + end => $self.ptr == end, + ) }; } macro_rules! len { ($self: ident) => {{ - #![allow(unused_unsafe)] // we're sometimes used within an unsafe block - - if T::IS_ZST { - $self.end.addr() - } else { - // To get rid of some bounds checks (see `position`), we use ptr_sub instead of - // offset_from (Tested by `codegen/slice-position-bounds-check`.) - // SAFETY: by the type invariant pointers are aligned and `start <= end` - unsafe { $self.end.sub_ptr($self.ptr.as_ptr()) } - } + if_zst!($self, + len => len, + end => { + // To get rid of some bounds checks (see `position`), we use ptr_sub instead of + // offset_from (Tested by `codegen/slice-position-bounds-check`.) + // SAFETY: by the type invariant pointers are aligned and `start <= end` + unsafe { end.sub_ptr($self.ptr) } + }, + ) }}; } @@ -50,20 +67,21 @@ macro_rules! iterator { $elem:ty, $raw_mut:tt, {$( $mut_:tt )?}, + $into_ref:ident, {$($extra:tt)*} ) => { // Returns the first element and moves the start of the iterator forwards by 1. // Greatly improves performance compared to an inlined function. The iterator // must not be empty. macro_rules! next_unchecked { - ($self: ident) => {& $( $mut_ )? *$self.post_inc_start(1)} + ($self: ident) => { $self.post_inc_start(1).$into_ref() } } // Returns the last element and moves the end of the iterator backwards by 1. // Greatly improves performance compared to an inlined function. The iterator // must not be empty. macro_rules! next_back_unchecked { - ($self: ident) => {& $( $mut_ )? *$self.pre_dec_end(1)} + ($self: ident) => { $self.pre_dec_end(1).$into_ref() } } impl<'a, T> $name<'a, T> { @@ -80,33 +98,40 @@ macro_rules! iterator { // returning the old start. // Unsafe because the offset must not exceed `self.len()`. #[inline(always)] - unsafe fn post_inc_start(&mut self, offset: usize) -> * $raw_mut T { + unsafe fn post_inc_start(&mut self, offset: usize) -> NonNull { let old = self.ptr; - if T::IS_ZST { - zst_shrink!(self, offset); - } else { - // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`, - // so this new pointer is inside `self` and thus guaranteed to be non-null. - self.ptr = unsafe { self.ptr.add(offset) }; + + // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`, + // so this new pointer is inside `self` and thus guaranteed to be non-null. + unsafe { + if_zst!(mut self, + len => *len = len.unchecked_sub(offset), + _end => self.ptr = self.ptr.add(offset), + ); } - old.as_ptr() + old } // Helper function for moving the end of the iterator backwards by `offset` elements, // returning the new end. // Unsafe because the offset must not exceed `self.len()`. #[inline(always)] - unsafe fn pre_dec_end(&mut self, offset: usize) -> * $raw_mut T { - if T::IS_ZST { - zst_shrink!(self, offset); - self.ptr.as_ptr() - } else { + unsafe fn pre_dec_end(&mut self, offset: usize) -> NonNull { + if_zst!(mut self, + // SAFETY: By our precondition, `offset` can be at most the + // current length, so the subtraction can never overflow. + len => unsafe { + *len = len.unchecked_sub(offset); + self.ptr + }, // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`, // which is guaranteed to not overflow an `isize`. Also, the resulting pointer // is in bounds of `slice`, which fulfills the other requirements for `offset`. - self.end = unsafe { self.end.sub(offset) }; - self.end - } + end => unsafe { + *end = end.sub(offset); + *end + }, + ) } } @@ -131,13 +156,9 @@ macro_rules! iterator { fn next(&mut self) -> Option<$elem> { // could be implemented with slices, but this avoids bounds checks - // SAFETY: `assume` call is safe because slices over non-ZSTs must - // have a non-null end pointer. The call to `next_unchecked!` is + // SAFETY: The call to `next_unchecked!` is // safe since we check if the iterator is empty first. unsafe { - if !::IS_ZST { - assume(!self.end.is_null()); - } if is_empty!(self) { None } else { @@ -161,14 +182,10 @@ macro_rules! iterator { fn nth(&mut self, n: usize) -> Option<$elem> { if n >= len!(self) { // This iterator is now empty. - if T::IS_ZST { - zst_set_len!(self, 0); - } else { - // SAFETY: end can't be 0 if T isn't ZST because ptr isn't 0 and end >= ptr - unsafe { - self.ptr = NonNull::new_unchecked(self.end as *mut T); - } - } + if_zst!(mut self, + len => *len = 0, + end => self.ptr = *end, + ); return None; } // SAFETY: We are in bounds. `post_inc_start` does the right thing even for ZSTs. @@ -375,13 +392,9 @@ macro_rules! iterator { fn next_back(&mut self) -> Option<$elem> { // could be implemented with slices, but this avoids bounds checks - // SAFETY: `assume` call is safe because slices over non-ZSTs must - // have a non-null end pointer. The call to `next_back_unchecked!` + // SAFETY: The call to `next_back_unchecked!` // is safe since we check if the iterator is empty first. unsafe { - if !::IS_ZST { - assume(!self.end.is_null()); - } if is_empty!(self) { None } else { @@ -394,11 +407,10 @@ macro_rules! iterator { fn nth_back(&mut self, n: usize) -> Option<$elem> { if n >= len!(self) { // This iterator is now empty. - if T::IS_ZST { - zst_set_len!(self, 0); - } else { - self.end = self.ptr.as_ptr(); - } + if_zst!(mut self, + len => *len = 0, + end => *end = self.ptr, + ); return None; } // SAFETY: We are in bounds. `pre_dec_end` does the right thing even for ZSTs. diff --git a/tests/codegen/iter-repeat-n-trivial-drop.rs b/tests/codegen/iter-repeat-n-trivial-drop.rs index 68b10934a0d8..0b08e578151d 100644 --- a/tests/codegen/iter-repeat-n-trivial-drop.rs +++ b/tests/codegen/iter-repeat-n-trivial-drop.rs @@ -34,6 +34,7 @@ pub fn iter_repeat_n_next(it: &mut std::iter::RepeatN) -> Option) -> bool { // CHECK-NOT: sub - // CHECK: %_0 = icmp eq {{i8\*|ptr}} {{%1|%0}}, {{%1|%0}} - // CHECK: ret i1 %_0 + // CHECK: %[[RET:.+]] = icmp eq {{i8\*|ptr}} {{%1|%0}}, {{%1|%0}} + // CHECK: ret i1 %[[RET]] y.len() == 0 } diff --git a/tests/codegen/slice-iter-nonnull.rs b/tests/codegen/slice-iter-nonnull.rs index 997bdaf56363..f7d164bc8564 100644 --- a/tests/codegen/slice-iter-nonnull.rs +++ b/tests/codegen/slice-iter-nonnull.rs @@ -2,11 +2,16 @@ // compile-flags: -O // ignore-debug (these add extra checks that make it hard to verify) #![crate_type = "lib"] +#![feature(exact_size_is_empty)] // The slice iterator used to `assume` that the `start` pointer was non-null. // That ought to be unneeded, though, since the type is `NonNull`, so this test // confirms that the appropriate metadata is included to denote that. +// It also used to `assume` the `end` pointer was non-null, but that's no longer +// needed as the code changed to read it as a `NonNull`, and thus gets the +// appropriate `!nonnull` annotations naturally. + // CHECK-LABEL: @slice_iter_next( #[no_mangle] pub fn slice_iter_next<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&'a u32> { @@ -75,3 +80,37 @@ pub fn slice_iter_mut_new(slice: &mut [u32]) -> std::slice::IterMut<'_, u32> { // CHECK: } slice.iter_mut() } + +// CHECK-LABEL: @slice_iter_is_empty +#[no_mangle] +pub fn slice_iter_is_empty(it: &std::slice::Iter<'_, u32>) -> bool { + // CHECK: %[[ENDP:.+]] = getelementptr{{.+}}ptr %it,{{.+}} 1 + // CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]] + // CHECK-SAME: !nonnull + // CHECK-SAME: !noundef + // CHECK: %[[START:.+]] = load ptr, ptr %it, + // CHECK-SAME: !nonnull + // CHECK-SAME: !noundef + + // CHECK: %[[RET:.+]] = icmp eq ptr %[[START]], %[[END]] + // CHECK: ret i1 %[[RET]] + it.is_empty() +} + +// CHECK-LABEL: @slice_iter_len +#[no_mangle] +pub fn slice_iter_len(it: &std::slice::Iter<'_, u32>) -> usize { + // CHECK: %[[START:.+]] = load ptr, ptr %it, + // CHECK-SAME: !nonnull + // CHECK-SAME: !noundef + // CHECK: %[[ENDP:.+]] = getelementptr{{.+}}ptr %it,{{.+}} 1 + // CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]] + // CHECK-SAME: !nonnull + // CHECK-SAME: !noundef + + // CHECK: ptrtoint + // CHECK: ptrtoint + // CHECK: sub nuw + // CHECK: lshr exact + it.len() +} 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 2cf81d862674..89009864c329 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 @@ -38,7 +38,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { scope 6 { let _7: *const T; scope 7 { - debug end => _7; + debug end_or_len => _7; scope 13 (inlined NonNull::::new_unchecked) { debug ptr => _9; let mut _10: *const T; @@ -138,7 +138,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { StorageDead(_9); StorageLive(_12); _12 = _7; - _13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> }; + _13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_12); StorageDead(_11); StorageDead(_7); 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 6985806ec938..836fa2677b18 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 @@ -38,7 +38,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { scope 6 { let _7: *const T; scope 7 { - debug end => _7; + debug end_or_len => _7; scope 13 (inlined NonNull::::new_unchecked) { debug ptr => _9; let mut _10: *const T; @@ -138,7 +138,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { StorageDead(_9); StorageLive(_12); _12 = _7; - _13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> }; + _13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_12); StorageDead(_11); StorageDead(_7); 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 a4b8460e98ea..146fa57a0b18 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 @@ -35,7 +35,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { scope 6 { let _7: *const T; scope 7 { - debug end => _7; + debug end_or_len => _7; scope 13 (inlined NonNull::::new_unchecked) { debug ptr => _9; let mut _10: *const T; @@ -128,7 +128,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageDead(_9); StorageLive(_12); _12 = _7; - _13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> }; + _13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_12); StorageDead(_11); StorageDead(_7); 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 58f312b1aac0..65baaf64a9e7 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 @@ -35,7 +35,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { scope 6 { let _7: *const T; scope 7 { - debug end => _7; + debug end_or_len => _7; scope 13 (inlined NonNull::::new_unchecked) { debug ptr => _9; let mut _10: *const T; @@ -128,7 +128,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageDead(_9); StorageLive(_12); _12 = _7; - _13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> }; + _13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_12); StorageDead(_11); StorageDead(_7); 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 b550711aa41d..a5df36ca3889 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 @@ -39,7 +39,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { scope 6 { let _7: *const T; scope 7 { - debug end => _7; + debug end_or_len => _7; scope 13 (inlined NonNull::::new_unchecked) { debug ptr => _9; let mut _10: *const T; @@ -139,7 +139,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageDead(_9); StorageLive(_12); _12 = _7; - _13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> }; + _13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_12); StorageDead(_11); StorageDead(_7); 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 23444241cd22..f681da4d275d 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 @@ -39,7 +39,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { scope 6 { let _7: *const T; scope 7 { - debug end => _7; + debug end_or_len => _7; scope 13 (inlined NonNull::::new_unchecked) { debug ptr => _9; let mut _10: *const T; @@ -139,7 +139,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { StorageDead(_9); StorageLive(_12); _12 = _7; - _13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> }; + _13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_12); StorageDead(_11); StorageDead(_7); From 12a2edd1498044eb06a722409a399ea7cc35d9df Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 7 Feb 2023 19:24:21 +0000 Subject: [PATCH 068/113] Always propagate into operands. --- .../rustc_mir_transform/src/const_prop.rs | 7 +--- .../aggregate.main.ConstProp.panic-abort.diff | 3 +- ...aggregate.main.ConstProp.panic-unwind.diff | 3 +- ...gate.main.PreCodegen.after.panic-abort.mir | 2 +- ...ate.main.PreCodegen.after.panic-unwind.mir | 2 +- ...ndex.main.ConstProp.32bit.panic-abort.diff | 2 +- ...dex.main.ConstProp.32bit.panic-unwind.diff | 2 +- ...ndex.main.ConstProp.64bit.panic-abort.diff | 2 +- ...dex.main.ConstProp.64bit.panic-unwind.diff | 2 +- ...iv_by_zero.main.ConstProp.panic-abort.diff | 5 +-- ...v_by_zero.main.ConstProp.panic-unwind.diff | 5 +-- ...od_by_zero.main.ConstProp.panic-abort.diff | 5 +-- ...d_by_zero.main.ConstProp.panic-unwind.diff | 5 +-- ...ices.main.ConstProp.32bit.panic-abort.diff | 2 +- ...ces.main.ConstProp.32bit.panic-unwind.diff | 2 +- ...ices.main.ConstProp.64bit.panic-abort.diff | 2 +- ...ces.main.ConstProp.64bit.panic-unwind.diff | 2 +- .../boxes.main.ConstProp.panic-abort.diff | 3 +- .../boxes.main.ConstProp.panic-unwind.diff | 3 +- .../indirect.main.ConstProp.panic-abort.diff | 2 +- .../indirect.main.ConstProp.panic-unwind.diff | 2 +- ...t_overflow.main.ConstProp.panic-abort.diff | 2 +- ..._overflow.main.ConstProp.panic-unwind.diff | 2 +- .../invalid_constant.main.ConstProp.diff | 3 +- ...ndex.main.ConstProp.32bit.panic-abort.diff | 2 +- ...dex.main.ConstProp.32bit.panic-unwind.diff | 2 +- ...ndex.main.ConstProp.64bit.panic-abort.diff | 2 +- ...dex.main.ConstProp.64bit.panic-unwind.diff | 2 +- ...set_of.concrete.ConstProp.panic-abort.diff | 12 ++++--- ...et_of.concrete.ConstProp.panic-unwind.diff | 12 ++++--- ...peat.main.ConstProp.32bit.panic-abort.diff | 2 +- ...eat.main.ConstProp.32bit.panic-unwind.diff | 2 +- ...peat.main.ConstProp.64bit.panic-abort.diff | 2 +- ...eat.main.ConstProp.64bit.panic-unwind.diff | 2 +- ...ropagation.main.ConstProp.panic-abort.diff | 3 +- ...opagation.main.ConstProp.panic-unwind.diff | 3 +- ..._len.main.ConstProp.32bit.panic-abort.diff | 2 +- ...len.main.ConstProp.32bit.panic-unwind.diff | 2 +- ..._len.main.ConstProp.64bit.panic-abort.diff | 2 +- ...len.main.ConstProp.64bit.panic-unwind.diff | 2 +- ...l_unsigned_smaller.Inline.panic-abort.diff | 6 +--- ..._unsigned_smaller.Inline.panic-unwind.diff | 6 +--- ...d_smaller.PreCodegen.after.panic-abort.mir | 16 ++++----- ..._smaller.PreCodegen.after.panic-unwind.mir | 16 ++++----- ...shr_signed_smaller.Inline.panic-abort.diff | 6 +--- ...hr_signed_smaller.Inline.panic-unwind.diff | 6 +--- ...d_smaller.PreCodegen.after.panic-abort.mir | 16 ++++----- ..._smaller.PreCodegen.after.panic-unwind.mir | 16 ++++----- ...ecked_ops.checked_shl.PreCodegen.after.mir | 34 ++++++++----------- .../intrinsics.f_u64.PreCodegen.after.mir | 8 ++--- 50 files changed, 114 insertions(+), 140 deletions(-) diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 6437a227d47c..da6ccf21144e 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -810,12 +810,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) { self.super_operand(operand, location); - - // Only const prop copies and moves on `mir_opt_level=3` as doing so - // currently slightly increases compile time in some cases. - if self.tcx.sess.mir_opt_level() >= 3 { - self.propagate_operand(operand) - } + self.propagate_operand(operand) } fn process_projection_elem( diff --git a/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-abort.diff index 0fad23104429..a4911a6d48ad 100644 --- a/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-abort.diff +++ b/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-abort.diff @@ -26,8 +26,9 @@ StorageLive(_4); StorageLive(_5); - _5 = _1; +- _4 = foo(move _5) -> [return: bb1, unwind unreachable]; + _5 = const 1_u8; - _4 = foo(move _5) -> [return: bb1, unwind unreachable]; ++ _4 = foo(const 1_u8) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-unwind.diff index e4650046bdf1..b8b9fa5cc1c5 100644 --- a/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-unwind.diff +++ b/tests/mir-opt/const_prop/aggregate.main.ConstProp.panic-unwind.diff @@ -26,8 +26,9 @@ StorageLive(_4); StorageLive(_5); - _5 = _1; +- _4 = foo(move _5) -> [return: bb1, unwind continue]; + _5 = const 1_u8; - _4 = foo(move _5) -> [return: bb1, unwind continue]; ++ _4 = foo(const 1_u8) -> [return: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-abort.mir b/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-abort.mir index 2ab6c1bf34dc..44a85a5636b9 100644 --- a/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-abort.mir @@ -23,7 +23,7 @@ fn main() -> () { StorageLive(_4); StorageLive(_5); _5 = const 1_u8; - _4 = foo(move _5) -> [return: bb1, unwind unreachable]; + _4 = foo(const 1_u8) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-unwind.mir index 9590c7f90aa3..2c7bdbb5055b 100644 --- a/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/const_prop/aggregate.main.PreCodegen.after.panic-unwind.mir @@ -23,7 +23,7 @@ fn main() -> () { StorageLive(_4); StorageLive(_5); _5 = const 1_u8; - _4 = foo(move _5) -> [return: bb1, unwind continue]; + _4 = foo(const 1_u8) -> [return: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-abort.diff b/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-abort.diff index 012b11e0e38a..b2f58f8f771d 100644 --- a/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-abort.diff @@ -23,7 +23,7 @@ - assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable]; + _4 = const 4_usize; + _5 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable]; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-unwind.diff index ec11395c3762..f9e3f8f171ad 100644 --- a/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-unwind.diff @@ -23,7 +23,7 @@ - assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue]; + _4 = const 4_usize; + _5 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue]; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-abort.diff b/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-abort.diff index 012b11e0e38a..b2f58f8f771d 100644 --- a/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-abort.diff @@ -23,7 +23,7 @@ - assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable]; + _4 = const 4_usize; + _5 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable]; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-unwind.diff index ec11395c3762..f9e3f8f171ad 100644 --- a/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-unwind.diff @@ -23,7 +23,7 @@ - assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue]; + _4 = const 4_usize; + _5 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue]; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-abort.diff index 34163d4d84a7..cead70110dcb 100644 --- a/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-abort.diff +++ b/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-abort.diff @@ -38,11 +38,12 @@ + _5 = const false; + _6 = const false; + _7 = const false; -+ assert(!const false, "attempt to compute `{} / {}`, which would overflow", const 1_i32, _3) -> [success: bb2, unwind unreachable]; ++ assert(!const false, "attempt to compute `{} / {}`, which would overflow", const 1_i32, const 0_i32) -> [success: bb2, unwind unreachable]; } bb2: { - _2 = Div(const 1_i32, move _3); +- _2 = Div(const 1_i32, move _3); ++ _2 = Div(const 1_i32, const 0_i32); StorageDead(_3); _0 = const (); StorageDead(_2); diff --git a/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-unwind.diff index a5b51681ec97..c9c4ba8548c6 100644 --- a/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-unwind.diff +++ b/tests/mir-opt/const_prop/bad_op_div_by_zero.main.ConstProp.panic-unwind.diff @@ -38,11 +38,12 @@ + _5 = const false; + _6 = const false; + _7 = const false; -+ assert(!const false, "attempt to compute `{} / {}`, which would overflow", const 1_i32, _3) -> [success: bb2, unwind continue]; ++ assert(!const false, "attempt to compute `{} / {}`, which would overflow", const 1_i32, const 0_i32) -> [success: bb2, unwind continue]; } bb2: { - _2 = Div(const 1_i32, move _3); +- _2 = Div(const 1_i32, move _3); ++ _2 = Div(const 1_i32, const 0_i32); StorageDead(_3); _0 = const (); StorageDead(_2); diff --git a/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-abort.diff index eb1c7d34f0f5..2666fd9eb91f 100644 --- a/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-abort.diff +++ b/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-abort.diff @@ -38,11 +38,12 @@ + _5 = const false; + _6 = const false; + _7 = const false; -+ assert(!const false, "attempt to compute the remainder of `{} % {}`, which would overflow", const 1_i32, _3) -> [success: bb2, unwind unreachable]; ++ assert(!const false, "attempt to compute the remainder of `{} % {}`, which would overflow", const 1_i32, const 0_i32) -> [success: bb2, unwind unreachable]; } bb2: { - _2 = Rem(const 1_i32, move _3); +- _2 = Rem(const 1_i32, move _3); ++ _2 = Rem(const 1_i32, const 0_i32); StorageDead(_3); _0 = const (); StorageDead(_2); diff --git a/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-unwind.diff index 4afddf3c92d7..679df90f16fe 100644 --- a/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-unwind.diff +++ b/tests/mir-opt/const_prop/bad_op_mod_by_zero.main.ConstProp.panic-unwind.diff @@ -38,11 +38,12 @@ + _5 = const false; + _6 = const false; + _7 = const false; -+ assert(!const false, "attempt to compute the remainder of `{} % {}`, which would overflow", const 1_i32, _3) -> [success: bb2, unwind continue]; ++ assert(!const false, "attempt to compute the remainder of `{} % {}`, which would overflow", const 1_i32, const 0_i32) -> [success: bb2, unwind continue]; } bb2: { - _2 = Rem(const 1_i32, move _3); +- _2 = Rem(const 1_i32, move _3); ++ _2 = Rem(const 1_i32, const 0_i32); StorageDead(_3); _0 = const (); StorageDead(_2); diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff index 30402df47c2c..55c774d555d4 100644 --- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff @@ -38,7 +38,7 @@ - _8 = Lt(_6, _7); - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable]; + _8 = const false; -+ assert(const false, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable]; ++ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff index 16d62daed299..dcab570ea776 100644 --- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff @@ -38,7 +38,7 @@ - _8 = Lt(_6, _7); - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue]; + _8 = const false; -+ assert(const false, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue]; ++ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff index 30402df47c2c..55c774d555d4 100644 --- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff @@ -38,7 +38,7 @@ - _8 = Lt(_6, _7); - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable]; + _8 = const false; -+ assert(const false, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable]; ++ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff index 16d62daed299..dcab570ea776 100644 --- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff @@ -38,7 +38,7 @@ - _8 = Lt(_6, _7); - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue]; + _8 = const false; -+ assert(const false, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue]; ++ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-abort.diff index 24b4796949ae..c9670a5ee436 100644 --- a/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-abort.diff +++ b/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-abort.diff @@ -24,9 +24,10 @@ StorageLive(_3); - _4 = SizeOf(i32); - _5 = AlignOf(i32); +- _6 = alloc::alloc::exchange_malloc(move _4, move _5) -> [return: bb1, unwind unreachable]; + _4 = const 4_usize; + _5 = const 4_usize; - _6 = alloc::alloc::exchange_malloc(move _4, move _5) -> [return: bb1, unwind unreachable]; ++ _6 = alloc::alloc::exchange_malloc(const 4_usize, const 4_usize) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-unwind.diff index 6214766c7ee5..64fe72be5c89 100644 --- a/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-unwind.diff +++ b/tests/mir-opt/const_prop/boxes.main.ConstProp.panic-unwind.diff @@ -24,9 +24,10 @@ StorageLive(_3); - _4 = SizeOf(i32); - _5 = AlignOf(i32); +- _6 = alloc::alloc::exchange_malloc(move _4, move _5) -> [return: bb1, unwind continue]; + _4 = const 4_usize; + _5 = const 4_usize; - _6 = alloc::alloc::exchange_malloc(move _4, move _5) -> [return: bb1, unwind continue]; ++ _6 = alloc::alloc::exchange_malloc(const 4_usize, const 4_usize) -> [return: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-abort.diff index eef9282c2cf0..ca0ce2888cd6 100644 --- a/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-abort.diff +++ b/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-abort.diff @@ -18,7 +18,7 @@ - assert(!move (_3.1: bool), "attempt to compute `{} + {}`, which would overflow", move _2, const 1_u8) -> [success: bb1, unwind unreachable]; + _2 = const 2_u8; + _3 = const (3_u8, false); -+ assert(!const false, "attempt to compute `{} + {}`, which would overflow", move _2, const 1_u8) -> [success: bb1, unwind unreachable]; ++ assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_u8, const 1_u8) -> [success: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-unwind.diff index ccfa35f001b5..d63fb9255a32 100644 --- a/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-unwind.diff +++ b/tests/mir-opt/const_prop/indirect.main.ConstProp.panic-unwind.diff @@ -18,7 +18,7 @@ - assert(!move (_3.1: bool), "attempt to compute `{} + {}`, which would overflow", move _2, const 1_u8) -> [success: bb1, unwind continue]; + _2 = const 2_u8; + _3 = const (3_u8, false); -+ assert(!const false, "attempt to compute `{} + {}`, which would overflow", move _2, const 1_u8) -> [success: bb1, unwind continue]; ++ assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_u8, const 1_u8) -> [success: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-abort.diff index e0467e3fcff0..51e17cf690ac 100644 --- a/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-abort.diff +++ b/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-abort.diff @@ -23,7 +23,7 @@ - _4 = CheckedAdd(_2, _3); - assert(!move (_4.1: bool), "attempt to compute `{} + {}`, which would overflow", _2, _3) -> [success: bb1, unwind unreachable]; + _4 = const (0_u8, true); -+ assert(!const true, "attempt to compute `{} + {}`, which would overflow", _2, _3) -> [success: bb1, unwind unreachable]; ++ assert(!const true, "attempt to compute `{} + {}`, which would overflow", const u8::MAX, const 1_u8) -> [success: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-unwind.diff index 4f8e0f0f599b..5ef201497fbc 100644 --- a/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-unwind.diff +++ b/tests/mir-opt/const_prop/inherit_overflow.main.ConstProp.panic-unwind.diff @@ -23,7 +23,7 @@ - _4 = CheckedAdd(_2, _3); - assert(!move (_4.1: bool), "attempt to compute `{} + {}`, which would overflow", _2, _3) -> [success: bb1, unwind continue]; + _4 = const (0_u8, true); -+ assert(!const true, "attempt to compute `{} + {}`, which would overflow", _2, _3) -> [success: bb1, unwind continue]; ++ assert(!const true, "attempt to compute `{} + {}`, which would overflow", const u8::MAX, const 1_u8) -> [success: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/invalid_constant.main.ConstProp.diff b/tests/mir-opt/const_prop/invalid_constant.main.ConstProp.diff index 0c9d1f4a38a4..10e978a683ac 100644 --- a/tests/mir-opt/const_prop/invalid_constant.main.ConstProp.diff +++ b/tests/mir-opt/const_prop/invalid_constant.main.ConstProp.diff @@ -43,8 +43,9 @@ StorageLive(_5); _5 = InvalidTag { int: const 4_u32 }; - _4 = (_5.1: E); +- _3 = [move _4]; + _4 = const Scalar(0x00000004): E; - _3 = [move _4]; ++ _3 = [const Scalar(0x00000004): E]; StorageDead(_4); StorageDead(_5); nop; diff --git a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-abort.diff b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-abort.diff index 61ba52fb0d6f..20e2ee326986 100644 --- a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-abort.diff @@ -23,7 +23,7 @@ - assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable]; + _4 = const 5000_usize; + _5 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable]; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-unwind.diff index 658607116c87..1bdbbbf7863f 100644 --- a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-unwind.diff @@ -23,7 +23,7 @@ - assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue]; + _4 = const 5000_usize; + _5 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue]; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-abort.diff b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-abort.diff index 61ba52fb0d6f..20e2ee326986 100644 --- a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-abort.diff @@ -23,7 +23,7 @@ - assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable]; + _4 = const 5000_usize; + _5 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable]; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-unwind.diff index 658607116c87..1bdbbbf7863f 100644 --- a/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-unwind.diff @@ -23,7 +23,7 @@ - assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue]; + _4 = const 5000_usize; + _5 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue]; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff index 046a79b4bfba..c73d217aeec4 100644 --- a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff +++ b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff @@ -28,8 +28,9 @@ StorageLive(_1); StorageLive(_2); - _2 = OffsetOf(Alpha, [0]); +- _1 = must_use::(move _2) -> [return: bb1, unwind unreachable]; + _2 = const 4_usize; - _1 = must_use::(move _2) -> [return: bb1, unwind unreachable]; ++ _1 = must_use::(const 4_usize) -> [return: bb1, unwind unreachable]; } bb1: { @@ -37,8 +38,9 @@ StorageLive(_3); StorageLive(_4); - _4 = OffsetOf(Alpha, [1]); +- _3 = must_use::(move _4) -> [return: bb2, unwind unreachable]; + _4 = const 0_usize; - _3 = must_use::(move _4) -> [return: bb2, unwind unreachable]; ++ _3 = must_use::(const 0_usize) -> [return: bb2, unwind unreachable]; } bb2: { @@ -46,8 +48,9 @@ StorageLive(_5); StorageLive(_6); - _6 = OffsetOf(Alpha, [2, 0]); +- _5 = must_use::(move _6) -> [return: bb3, unwind unreachable]; + _6 = const 2_usize; - _5 = must_use::(move _6) -> [return: bb3, unwind unreachable]; ++ _5 = must_use::(const 2_usize) -> [return: bb3, unwind unreachable]; } bb3: { @@ -55,8 +58,9 @@ StorageLive(_7); StorageLive(_8); - _8 = OffsetOf(Alpha, [2, 1]); +- _7 = must_use::(move _8) -> [return: bb4, unwind unreachable]; + _8 = const 3_usize; - _7 = must_use::(move _8) -> [return: bb4, unwind unreachable]; ++ _7 = must_use::(const 3_usize) -> [return: bb4, unwind unreachable]; } bb4: { diff --git a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff index bbb807d8fcdc..913ffca4ae90 100644 --- a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff +++ b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff @@ -28,8 +28,9 @@ StorageLive(_1); StorageLive(_2); - _2 = OffsetOf(Alpha, [0]); +- _1 = must_use::(move _2) -> [return: bb1, unwind continue]; + _2 = const 4_usize; - _1 = must_use::(move _2) -> [return: bb1, unwind continue]; ++ _1 = must_use::(const 4_usize) -> [return: bb1, unwind continue]; } bb1: { @@ -37,8 +38,9 @@ StorageLive(_3); StorageLive(_4); - _4 = OffsetOf(Alpha, [1]); +- _3 = must_use::(move _4) -> [return: bb2, unwind continue]; + _4 = const 0_usize; - _3 = must_use::(move _4) -> [return: bb2, unwind continue]; ++ _3 = must_use::(const 0_usize) -> [return: bb2, unwind continue]; } bb2: { @@ -46,8 +48,9 @@ StorageLive(_5); StorageLive(_6); - _6 = OffsetOf(Alpha, [2, 0]); +- _5 = must_use::(move _6) -> [return: bb3, unwind continue]; + _6 = const 2_usize; - _5 = must_use::(move _6) -> [return: bb3, unwind continue]; ++ _5 = must_use::(const 2_usize) -> [return: bb3, unwind continue]; } bb3: { @@ -55,8 +58,9 @@ StorageLive(_7); StorageLive(_8); - _8 = OffsetOf(Alpha, [2, 1]); +- _7 = must_use::(move _8) -> [return: bb4, unwind continue]; + _8 = const 3_usize; - _7 = must_use::(move _8) -> [return: bb4, unwind continue]; ++ _7 = must_use::(const 3_usize) -> [return: bb4, unwind continue]; } bb4: { diff --git a/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-abort.diff b/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-abort.diff index e095dd01da67..a55bd029e99a 100644 --- a/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-abort.diff @@ -25,7 +25,7 @@ - assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable]; + _5 = const 8_usize; + _6 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable]; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-unwind.diff index 571f279a8c14..d49ef2e0179f 100644 --- a/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-unwind.diff @@ -25,7 +25,7 @@ - assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind continue]; + _5 = const 8_usize; + _6 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind continue]; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-abort.diff b/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-abort.diff index e095dd01da67..a55bd029e99a 100644 --- a/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-abort.diff @@ -25,7 +25,7 @@ - assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable]; + _5 = const 8_usize; + _6 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable]; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-unwind.diff index 571f279a8c14..d49ef2e0179f 100644 --- a/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-unwind.diff @@ -25,7 +25,7 @@ - assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind continue]; + _5 = const 8_usize; + _6 = const true; -+ assert(const true, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind continue]; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-abort.diff index 0677295d0785..c5c09c8edd7f 100644 --- a/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-abort.diff +++ b/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-abort.diff @@ -16,8 +16,9 @@ StorageLive(_2); StorageLive(_3); - _3 = _1; +- _2 = consume(move _3) -> [return: bb1, unwind unreachable]; + _3 = const 1_u32; - _2 = consume(move _3) -> [return: bb1, unwind unreachable]; ++ _2 = consume(const 1_u32) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-unwind.diff index a7d7a7224cea..b256c56765e3 100644 --- a/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-unwind.diff +++ b/tests/mir-opt/const_prop/scalar_literal_propagation.main.ConstProp.panic-unwind.diff @@ -16,8 +16,9 @@ StorageLive(_2); StorageLive(_3); - _3 = _1; +- _2 = consume(move _3) -> [return: bb1, unwind continue]; + _3 = const 1_u32; - _2 = consume(move _3) -> [return: bb1, unwind continue]; ++ _2 = consume(const 1_u32) -> [return: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-abort.diff b/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-abort.diff index 3c2b8e111cbe..c2e1288b41e4 100644 --- a/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-abort.diff @@ -30,7 +30,7 @@ - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _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 {}", move _7, _6) -> [success: bb1, unwind unreachable]; ++ 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: { diff --git a/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-unwind.diff index 303096030b48..23646c3c9762 100644 --- a/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-unwind.diff @@ -30,7 +30,7 @@ - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _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 {}", move _7, _6) -> [success: bb1, unwind continue]; ++ 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: { diff --git a/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-abort.diff b/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-abort.diff index 3c2b8e111cbe..c2e1288b41e4 100644 --- a/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-abort.diff @@ -30,7 +30,7 @@ - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _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 {}", move _7, _6) -> [success: bb1, unwind unreachable]; ++ 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: { diff --git a/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-unwind.diff index 303096030b48..23646c3c9762 100644 --- a/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/const_prop/slice_len.main.ConstProp.64bit.panic-unwind.diff @@ -30,7 +30,7 @@ - assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _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 {}", move _7, _6) -> [success: bb1, unwind continue]; ++ 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: { diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff index 90b32247c95e..d052219661b3 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff @@ -12,7 +12,6 @@ + debug rhs => _4; + let mut _5: u16; + let mut _6: bool; -+ let mut _7: u32; + scope 2 { + } + } @@ -28,10 +27,7 @@ - bb1: { + StorageLive(_5); + StorageLive(_6); -+ StorageLive(_7); -+ _7 = const 65535_u32; -+ _6 = Le(_4, move _7); -+ StorageDead(_7); ++ _6 = Le(_4, const 65535_u32); + assume(move _6); + StorageDead(_6); + _5 = _4 as u16 (IntToInt); diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff index cae25759cd8e..67a5ac2483b6 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff @@ -12,7 +12,6 @@ + debug rhs => _4; + let mut _5: u16; + let mut _6: bool; -+ let mut _7: u32; + scope 2 { + } + } @@ -28,10 +27,7 @@ - bb1: { + StorageLive(_5); + StorageLive(_6); -+ StorageLive(_7); -+ _7 = const 65535_u32; -+ _6 = Le(_4, move _7); -+ StorageDead(_7); ++ _6 = Le(_4, const 65535_u32); + assume(move _6); + StorageDead(_6); + _5 = _4 as u16 (IntToInt); diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir index 3f388a69d7eb..f9dff62e0c8a 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-abort.mir @@ -7,25 +7,21 @@ fn unchecked_shl_unsigned_smaller(_1: u16, _2: u32) -> u16 { scope 1 (inlined core::num::::unchecked_shl) { debug self => _1; debug rhs => _2; - let mut _3: u32; - let mut _4: bool; - let mut _5: u16; + let mut _3: bool; + let mut _4: u16; scope 2 { } } bb0: { - StorageLive(_5); StorageLive(_4); StorageLive(_3); - _3 = const 65535_u32; - _4 = Le(_2, move _3); + _3 = Le(_2, const 65535_u32); + assume(move _3); StorageDead(_3); - assume(move _4); + _4 = _2 as u16 (IntToInt); + _0 = ShlUnchecked(_1, move _4); StorageDead(_4); - _5 = _2 as u16 (IntToInt); - _0 = ShlUnchecked(_1, move _5); - StorageDead(_5); return; } } diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir index 3f388a69d7eb..f9dff62e0c8a 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.PreCodegen.after.panic-unwind.mir @@ -7,25 +7,21 @@ fn unchecked_shl_unsigned_smaller(_1: u16, _2: u32) -> u16 { scope 1 (inlined core::num::::unchecked_shl) { debug self => _1; debug rhs => _2; - let mut _3: u32; - let mut _4: bool; - let mut _5: u16; + let mut _3: bool; + let mut _4: u16; scope 2 { } } bb0: { - StorageLive(_5); StorageLive(_4); StorageLive(_3); - _3 = const 65535_u32; - _4 = Le(_2, move _3); + _3 = Le(_2, const 65535_u32); + assume(move _3); StorageDead(_3); - assume(move _4); + _4 = _2 as u16 (IntToInt); + _0 = ShlUnchecked(_1, move _4); StorageDead(_4); - _5 = _2 as u16 (IntToInt); - _0 = ShlUnchecked(_1, move _5); - StorageDead(_5); return; } } diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff index fa7e5d16e39f..15b36b284de5 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-abort.diff @@ -12,7 +12,6 @@ + debug rhs => _4; + let mut _5: i16; + let mut _6: bool; -+ let mut _7: u32; + scope 2 { + } + } @@ -28,10 +27,7 @@ - bb1: { + StorageLive(_5); + StorageLive(_6); -+ StorageLive(_7); -+ _7 = const 32767_u32; -+ _6 = Le(_4, move _7); -+ StorageDead(_7); ++ _6 = Le(_4, const 32767_u32); + assume(move _6); + StorageDead(_6); + _5 = _4 as i16 (IntToInt); diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff index fe5331214866..8629f92dbad4 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.Inline.panic-unwind.diff @@ -12,7 +12,6 @@ + debug rhs => _4; + let mut _5: i16; + let mut _6: bool; -+ let mut _7: u32; + scope 2 { + } + } @@ -28,10 +27,7 @@ - bb1: { + StorageLive(_5); + StorageLive(_6); -+ StorageLive(_7); -+ _7 = const 32767_u32; -+ _6 = Le(_4, move _7); -+ StorageDead(_7); ++ _6 = Le(_4, const 32767_u32); + assume(move _6); + StorageDead(_6); + _5 = _4 as i16 (IntToInt); diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir index 64ea25349ac0..65fa0d956c06 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-abort.mir @@ -7,25 +7,21 @@ fn unchecked_shr_signed_smaller(_1: i16, _2: u32) -> i16 { scope 1 (inlined core::num::::unchecked_shr) { debug self => _1; debug rhs => _2; - let mut _3: u32; - let mut _4: bool; - let mut _5: i16; + let mut _3: bool; + let mut _4: i16; scope 2 { } } bb0: { - StorageLive(_5); StorageLive(_4); StorageLive(_3); - _3 = const 32767_u32; - _4 = Le(_2, move _3); + _3 = Le(_2, const 32767_u32); + assume(move _3); StorageDead(_3); - assume(move _4); + _4 = _2 as i16 (IntToInt); + _0 = ShrUnchecked(_1, move _4); StorageDead(_4); - _5 = _2 as i16 (IntToInt); - _0 = ShrUnchecked(_1, move _5); - StorageDead(_5); return; } } diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir index 64ea25349ac0..65fa0d956c06 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_smaller.PreCodegen.after.panic-unwind.mir @@ -7,25 +7,21 @@ fn unchecked_shr_signed_smaller(_1: i16, _2: u32) -> i16 { scope 1 (inlined core::num::::unchecked_shr) { debug self => _1; debug rhs => _2; - let mut _3: u32; - let mut _4: bool; - let mut _5: i16; + let mut _3: bool; + let mut _4: i16; scope 2 { } } bb0: { - StorageLive(_5); StorageLive(_4); StorageLive(_3); - _3 = const 32767_u32; - _4 = Le(_2, move _3); + _3 = Le(_2, const 32767_u32); + assume(move _3); StorageDead(_3); - assume(move _4); + _4 = _2 as i16 (IntToInt); + _0 = ShrUnchecked(_1, move _4); StorageDead(_4); - _5 = _2 as i16 (IntToInt); - _0 = ShrUnchecked(_1, move _5); - StorageDead(_5); return; } } diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir index 1228114eab87..8304cb45b354 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir @@ -7,25 +7,24 @@ fn checked_shl(_1: u32, _2: u32) -> Option { scope 1 (inlined core::num::::checked_shl) { debug self => _1; debug rhs => _2; - let mut _7: bool; + let mut _6: bool; scope 2 { - debug a => _5; - debug b => _6; + debug a => _4; + debug b => _5; } scope 3 (inlined core::num::::overflowing_shl) { debug self => _1; debug rhs => _2; - let mut _5: u32; - let mut _6: bool; + let mut _4: u32; + let mut _5: bool; scope 4 (inlined core::num::::wrapping_shl) { debug self => _1; debug rhs => _2; let mut _3: u32; - let mut _4: u32; scope 5 { scope 6 (inlined core::num::::unchecked_shl) { debug self => _1; - debug rhs => _4; + debug rhs => _3; scope 7 { } } @@ -35,26 +34,23 @@ fn checked_shl(_1: u32, _2: u32) -> Option { } bb0: { - StorageLive(_5); - StorageLive(_6); StorageLive(_4); + StorageLive(_5); StorageLive(_3); - _3 = const 31_u32; - _4 = BitAnd(_2, move _3); + _3 = BitAnd(_2, const 31_u32); + _4 = ShlUnchecked(_1, _3); StorageDead(_3); - _5 = ShlUnchecked(_1, _4); - StorageDead(_4); - _6 = Ge(_2, const _); - StorageLive(_7); - _7 = unlikely(move _6) -> [return: bb1, unwind unreachable]; + _5 = Ge(_2, const _); + StorageLive(_6); + _6 = unlikely(move _5) -> [return: bb1, unwind unreachable]; } bb1: { - switchInt(move _7) -> [0: bb2, otherwise: bb3]; + switchInt(move _6) -> [0: bb2, otherwise: bb3]; } bb2: { - _0 = Option::::Some(_5); + _0 = Option::::Some(_4); goto -> bb4; } @@ -64,9 +60,9 @@ fn checked_shl(_1: u32, _2: u32) -> Option { } bb4: { - StorageDead(_7); StorageDead(_6); StorageDead(_5); + StorageDead(_4); return; } } diff --git a/tests/mir-opt/pre-codegen/intrinsics.f_u64.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/intrinsics.f_u64.PreCodegen.after.mir index 66acbbbb21e5..174fb2c0c3c0 100644 --- a/tests/mir-opt/pre-codegen/intrinsics.f_u64.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/intrinsics.f_u64.PreCodegen.after.mir @@ -2,22 +2,18 @@ fn f_u64() -> () { let mut _0: (); - let mut _1: u64; scope 1 (inlined f_dispatch::) { debug t => const 0_u64; - let _2: (); + let _1: (); scope 2 (inlined std::mem::size_of::) { } } bb0: { - StorageLive(_1); - _1 = const 0_u64; - _2 = f_non_zst::(move _1) -> [return: bb1, unwind unreachable]; + _1 = f_non_zst::(const 0_u64) -> [return: bb1, unwind unreachable]; } bb1: { - StorageDead(_1); return; } } From 895e2159f880e853555c8693f697401efff80ade Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 8 Feb 2023 18:54:34 +0000 Subject: [PATCH 069/113] Also propagate ScalarPair operands. --- .../rustc_mir_transform/src/const_prop.rs | 145 ++++++++---------- ...ification.hello.ConstProp.panic-abort.diff | 3 +- ...fication.hello.ConstProp.panic-unwind.diff | 3 +- ...ssue_67019.main.ConstProp.panic-abort.diff | 3 +- ...sue_67019.main.ConstProp.panic-unwind.diff | 3 +- ...ropagation.main.ConstProp.panic-abort.diff | 3 +- ...opagation.main.ConstProp.panic-unwind.diff | 3 +- 7 files changed, 72 insertions(+), 91 deletions(-) diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index da6ccf21144e..2767fe30c6fe 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -14,8 +14,7 @@ use rustc_middle::mir::visit::{ }; use rustc_middle::mir::*; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; -use rustc_middle::ty::GenericArgs; -use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, GenericArgs, Instance, ParamEnv, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::{def_id::DefId, Span, DUMMY_SP}; use rustc_target::abi::{self, Align, HasDataLayout, Size, TargetDataLayout}; use rustc_target::spec::abi::Abi as CallAbi; @@ -434,24 +433,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } fn propagate_operand(&mut self, operand: &mut Operand<'tcx>) { - match *operand { - Operand::Copy(l) | Operand::Move(l) => { - if let Some(value) = self.get_const(l) && self.should_const_prop(&value) { - // FIXME(felix91gr): this code only handles `Scalar` cases. - // For now, we're not handling `ScalarPair` cases because - // doing so here would require a lot of code duplication. - // We should hopefully generalize `Operand` handling into a fn, - // and use it to do const-prop here and everywhere else - // where it makes sense. - if let interpret::Operand::Immediate(interpret::Immediate::Scalar( - scalar, - )) = *value - { - *operand = self.operand_from_scalar(scalar, value.layout.ty); - } - } - } - Operand::Constant(_) => (), + if let Some(place) = operand.place() && let Some(op) = self.replace_with_const(place) { + *operand = op; } } @@ -579,78 +562,63 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { })) } - fn replace_with_const(&mut self, place: Place<'tcx>, rval: &mut Rvalue<'tcx>) { + fn replace_with_const(&mut self, place: Place<'tcx>) -> Option> { // This will return None if the above `const_prop` invocation only "wrote" a // type whose creation requires no write. E.g. a generator whose initial state // consists solely of uninitialized memory (so it doesn't capture any locals). - let Some(ref value) = self.get_const(place) else { return }; - if !self.should_const_prop(value) { - return; + let value = self.get_const(place)?; + if !self.should_const_prop(&value) { + return None; } - trace!("replacing {:?}={:?} with {:?}", place, rval, value); + trace!("replacing {:?} with {:?}", place, value); - if let Rvalue::Use(Operand::Constant(c)) = rval { - match c.literal { - ConstantKind::Ty(c) if matches!(c.kind(), ConstKind::Unevaluated(..)) => {} - _ => { - trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c); - return; - } - } - } - - trace!("attempting to replace {:?} with {:?}", rval, value); // FIXME> figure out what to do when read_immediate_raw fails - let imm = self.ecx.read_immediate_raw(value).ok(); + let imm = self.ecx.read_immediate_raw(&value).ok()?; - if let Some(Right(imm)) = imm { - match *imm { - interpret::Immediate::Scalar(scalar) => { - *rval = Rvalue::Use(self.operand_from_scalar(scalar, value.layout.ty)); - } - Immediate::ScalarPair(..) => { - // Found a value represented as a pair. For now only do const-prop if the type - // of `rvalue` is also a tuple with two scalars. - // FIXME: enable the general case stated above ^. - let ty = value.layout.ty; - // Only do it for tuples - if let ty::Tuple(types) = ty.kind() { - // Only do it if tuple is also a pair with two scalars - if let [ty1, ty2] = types[..] { - let ty_is_scalar = |ty| { - self.ecx.layout_of(ty).ok().map(|layout| layout.abi.is_scalar()) - == Some(true) - }; - let alloc = if ty_is_scalar(ty1) && ty_is_scalar(ty2) { - let alloc = self - .ecx - .intern_with_temp_alloc(value.layout, |ecx, dest| { - ecx.write_immediate(*imm, dest) - }) - .unwrap(); - Some(alloc) - } else { - None - }; - - if let Some(alloc) = alloc { - // Assign entire constant in a single statement. - // We can't use aggregates, as we run after the aggregate-lowering `MirPhase`. - let const_val = ConstValue::ByRef { alloc, offset: Size::ZERO }; - let literal = ConstantKind::Val(const_val, ty); - *rval = Rvalue::Use(Operand::Constant(Box::new(Constant { - span: DUMMY_SP, - user_ty: None, - literal, - }))); - } - } - } - } - // Scalars or scalar pairs that contain undef values are assumed to not have - // successfully evaluated and are thus not propagated. - _ => {} + let Right(imm) = imm else { return None }; + match *imm { + interpret::Immediate::Scalar(scalar) => { + Some(self.operand_from_scalar(scalar, value.layout.ty)) } + Immediate::ScalarPair(..) => { + // Found a value represented as a pair. For now only do const-prop if the type + // of `rvalue` is also a tuple with two scalars. + // FIXME: enable the general case stated above ^. + let ty = value.layout.ty; + // Only do it for tuples + let ty::Tuple(types) = ty.kind() else { return None }; + // Only do it if tuple is also a pair with two scalars + if let [ty1, ty2] = types[..] { + let ty_is_scalar = |ty| { + self.ecx.layout_of(ty).ok().map(|layout| layout.abi.is_scalar()) + == Some(true) + }; + let alloc = if ty_is_scalar(ty1) && ty_is_scalar(ty2) { + self.ecx + .intern_with_temp_alloc(value.layout, |ecx, dest| { + ecx.write_immediate(*imm, dest) + }) + .unwrap() + } else { + return None; + }; + + // Assign entire constant in a single statement. + // We can't use aggregates, as we run after the aggregate-lowering `MirPhase`. + let const_val = ConstValue::ByRef { alloc, offset: Size::ZERO }; + let literal = ConstantKind::Val(const_val, ty); + Some(Operand::Constant(Box::new(Constant { + span: DUMMY_SP, + user_ty: None, + literal, + }))) + } else { + None + } + } + // Scalars or scalar pairs that contain undef values are assumed to not have + // successfully evaluated and are thus not propagated. + _ => None, } } @@ -847,7 +815,14 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { ConstPropMode::NoPropagation => self.ensure_not_propagated(place.local), ConstPropMode::OnlyInsideOwnBlock | ConstPropMode::FullConstProp => { if let Some(()) = self.eval_rvalue_with_identities(rvalue, *place) { - self.replace_with_const(*place, rvalue); + // If this was already an evaluated constant, keep it. + if let Rvalue::Use(Operand::Constant(c)) = rvalue + && let ConstantKind::Val(..) = c.literal + { + trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c); + } else if let Some(operand) = self.replace_with_const(*place) { + *rvalue = Rvalue::Use(operand); + } } else { // Const prop failed, so erase the destination, ensuring that whatever happens // from here on, does not know about the previous value. diff --git a/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-abort.diff index e77c09848b7b..ba2e89f0a744 100644 --- a/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-abort.diff +++ b/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-abort.diff @@ -8,8 +8,9 @@ bb0: { StorageLive(_1); - _1 = const _; +- _1 = const _; - switchInt(move _1) -> [0: bb2, otherwise: bb1]; ++ _1 = const false; + switchInt(const false) -> [0: bb2, otherwise: bb1]; } diff --git a/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-unwind.diff index 7496d2543097..e0a610f60a79 100644 --- a/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-unwind.diff +++ b/tests/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.panic-unwind.diff @@ -8,8 +8,9 @@ bb0: { StorageLive(_1); - _1 = const _; +- _1 = const _; - switchInt(move _1) -> [0: bb2, otherwise: bb1]; ++ _1 = const false; + switchInt(const false) -> [0: bb2, otherwise: bb1]; } diff --git a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff index 96b4093726c8..0b03f3f71d37 100644 --- a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff +++ b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff @@ -11,8 +11,9 @@ StorageLive(_2); StorageLive(_3); - _3 = (const 1_u8, const 2_u8); +- _2 = (move _3,); + _3 = const (1_u8, 2_u8); - _2 = (move _3,); ++ _2 = (const (1_u8, 2_u8),); StorageDead(_3); _1 = test(move _2) -> [return: bb1, unwind unreachable]; } diff --git a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff index 95776030162e..97215425ee6e 100644 --- a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff +++ b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff @@ -11,8 +11,9 @@ StorageLive(_2); StorageLive(_3); - _3 = (const 1_u8, const 2_u8); +- _2 = (move _3,); + _3 = const (1_u8, 2_u8); - _2 = (move _3,); ++ _2 = (const (1_u8, 2_u8),); StorageDead(_3); _1 = test(move _2) -> [return: bb1, unwind continue]; } diff --git a/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-abort.diff index a72f24152fb4..9e705695ac0c 100644 --- a/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-abort.diff +++ b/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-abort.diff @@ -17,8 +17,9 @@ StorageLive(_2); StorageLive(_3); - _3 = _1; +- _2 = consume(move _3) -> [return: bb1, unwind unreachable]; + _3 = const (1_u32, 2_u32); - _2 = consume(move _3) -> [return: bb1, unwind unreachable]; ++ _2 = consume(const (1_u32, 2_u32)) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-unwind.diff index 6255f9ec5962..882dd97cc16b 100644 --- a/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-unwind.diff +++ b/tests/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.panic-unwind.diff @@ -17,8 +17,9 @@ StorageLive(_2); StorageLive(_3); - _3 = _1; +- _2 = consume(move _3) -> [return: bb1, unwind continue]; + _3 = const (1_u32, 2_u32); - _2 = consume(move _3) -> [return: bb1, unwind continue]; ++ _2 = consume(const (1_u32, 2_u32)) -> [return: bb1, unwind continue]; } bb1: { From ccfa9af29d54189714d743fe04286f3825a36d1e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 8 Feb 2023 21:05:56 +0000 Subject: [PATCH 070/113] Propagate ScalarPair for any type. --- .../rustc_mir_transform/src/const_prop.rs | 48 ++++++------------- ...ssue_66971.main.ConstProp.panic-abort.diff | 6 ++- ...sue_66971.main.ConstProp.panic-unwind.diff | 6 ++- ...ssue_67019.main.ConstProp.panic-abort.diff | 5 +- ...sue_67019.main.ConstProp.panic-unwind.diff | 5 +- 5 files changed, 29 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 2767fe30c6fe..cb01a835b6ce 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -581,40 +581,22 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { Some(self.operand_from_scalar(scalar, value.layout.ty)) } Immediate::ScalarPair(..) => { - // Found a value represented as a pair. For now only do const-prop if the type - // of `rvalue` is also a tuple with two scalars. - // FIXME: enable the general case stated above ^. - let ty = value.layout.ty; - // Only do it for tuples - let ty::Tuple(types) = ty.kind() else { return None }; - // Only do it if tuple is also a pair with two scalars - if let [ty1, ty2] = types[..] { - let ty_is_scalar = |ty| { - self.ecx.layout_of(ty).ok().map(|layout| layout.abi.is_scalar()) - == Some(true) - }; - let alloc = if ty_is_scalar(ty1) && ty_is_scalar(ty2) { - self.ecx - .intern_with_temp_alloc(value.layout, |ecx, dest| { - ecx.write_immediate(*imm, dest) - }) - .unwrap() - } else { - return None; - }; + let alloc = self + .ecx + .intern_with_temp_alloc(value.layout, |ecx, dest| { + ecx.write_immediate(*imm, dest) + }) + .ok()?; - // Assign entire constant in a single statement. - // We can't use aggregates, as we run after the aggregate-lowering `MirPhase`. - let const_val = ConstValue::ByRef { alloc, offset: Size::ZERO }; - let literal = ConstantKind::Val(const_val, ty); - Some(Operand::Constant(Box::new(Constant { - span: DUMMY_SP, - user_ty: None, - literal, - }))) - } else { - None - } + let literal = ConstantKind::Val( + ConstValue::ByRef { alloc, offset: Size::ZERO }, + value.layout.ty, + ); + Some(Operand::Constant(Box::new(Constant { + span: DUMMY_SP, + user_ty: None, + literal, + }))) } // Scalars or scalar pairs that contain undef values are assumed to not have // successfully evaluated and are thus not propagated. diff --git a/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-abort.diff index 516f13586d3f..170c019782df 100644 --- a/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-abort.diff +++ b/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-abort.diff @@ -8,8 +8,10 @@ bb0: { StorageLive(_2); - _2 = (const (), const 0_u8, const 0_u8); - _1 = encode(move _2) -> [return: bb1, unwind unreachable]; +- _2 = (const (), const 0_u8, const 0_u8); +- _1 = encode(move _2) -> [return: bb1, unwind unreachable]; ++ _2 = const ((), 0_u8, 0_u8); ++ _1 = encode(const ((), 0_u8, 0_u8)) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-unwind.diff index 5e3443228cd2..64227dfd78c2 100644 --- a/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-unwind.diff +++ b/tests/mir-opt/const_prop/issue_66971.main.ConstProp.panic-unwind.diff @@ -8,8 +8,10 @@ bb0: { StorageLive(_2); - _2 = (const (), const 0_u8, const 0_u8); - _1 = encode(move _2) -> [return: bb1, unwind continue]; +- _2 = (const (), const 0_u8, const 0_u8); +- _1 = encode(move _2) -> [return: bb1, unwind continue]; ++ _2 = const ((), 0_u8, 0_u8); ++ _1 = encode(const ((), 0_u8, 0_u8)) -> [return: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff index 0b03f3f71d37..e1f3f37b3707 100644 --- a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff +++ b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-abort.diff @@ -13,9 +13,10 @@ - _3 = (const 1_u8, const 2_u8); - _2 = (move _3,); + _3 = const (1_u8, 2_u8); -+ _2 = (const (1_u8, 2_u8),); ++ _2 = const ((1_u8, 2_u8),); StorageDead(_3); - _1 = test(move _2) -> [return: bb1, unwind unreachable]; +- _1 = test(move _2) -> [return: bb1, unwind unreachable]; ++ _1 = test(const ((1_u8, 2_u8),)) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff index 97215425ee6e..aaa376a95cf6 100644 --- a/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff +++ b/tests/mir-opt/const_prop/issue_67019.main.ConstProp.panic-unwind.diff @@ -13,9 +13,10 @@ - _3 = (const 1_u8, const 2_u8); - _2 = (move _3,); + _3 = const (1_u8, 2_u8); -+ _2 = (const (1_u8, 2_u8),); ++ _2 = const ((1_u8, 2_u8),); StorageDead(_3); - _1 = test(move _2) -> [return: bb1, unwind continue]; +- _1 = test(move _2) -> [return: bb1, unwind continue]; ++ _1 = test(const ((1_u8, 2_u8),)) -> [return: bb1, unwind continue]; } bb1: { From 3f708add2f128d276fd53d422e0e79cd51188632 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 28 May 2023 10:16:05 +0000 Subject: [PATCH 071/113] Remove visit_terminator. --- .../rustc_mir_transform/src/const_prop.rs | 65 ------------------- 1 file changed, 65 deletions(-) diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index cb01a835b6ce..de9daf45269d 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -406,32 +406,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ecx.machine.written_only_inside_own_block_locals.remove(&local); } - /// Returns the value, if any, of evaluating `c`. - fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option> { - // FIXME we need to revisit this for #67176 - if c.has_param() { - return None; - } - - // No span, we don't want errors to be shown. - self.ecx.eval_mir_constant(&c.literal, None, None).ok() - } - - /// Returns the value, if any, of evaluating `place`. - fn eval_place(&mut self, place: Place<'tcx>) -> Option> { - trace!("eval_place(place={:?})", place); - self.ecx.eval_place_to_op(place, None).ok() - } - - /// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant` - /// or `eval_place`, depending on the variant of `Operand` used. - fn eval_operand(&mut self, op: &Operand<'tcx>) -> Option> { - match *op { - Operand::Constant(ref c) => self.eval_constant(c), - Operand::Move(place) | Operand::Copy(place) => self.eval_place(place), - } - } - fn propagate_operand(&mut self, operand: &mut Operand<'tcx>) { if let Some(place) = operand.place() && let Some(op) = self.replace_with_const(place) { *operand = op; @@ -871,45 +845,6 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { } } - fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { - self.super_terminator(terminator, location); - - match &mut terminator.kind { - TerminatorKind::Assert { expected, ref mut cond, .. } => { - if let Some(ref value) = self.eval_operand(&cond) - && let Ok(value_const) = self.ecx.read_scalar(&value) - && self.should_const_prop(value) - { - trace!("assertion on {:?} should be {:?}", value, expected); - *cond = self.operand_from_scalar(value_const, self.tcx.types.bool); - } - } - TerminatorKind::SwitchInt { ref mut discr, .. } => { - // FIXME: This is currently redundant with `visit_operand`, but sadly - // always visiting operands currently causes a perf regression in LLVM codegen, so - // `visit_operand` currently only runs for propagates places for `mir_opt_level=4`. - self.propagate_operand(discr) - } - // None of these have Operands to const-propagate. - TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Terminate - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::Drop { .. } - | TerminatorKind::Yield { .. } - | TerminatorKind::GeneratorDrop - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::InlineAsm { .. } => {} - // Every argument in our function calls have already been propagated in `visit_operand`. - // - // NOTE: because LLVM codegen gives slight performance regressions with it, so this is - // gated on `mir_opt_level=3`. - TerminatorKind::Call { .. } => {} - } - } - fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { self.super_basic_block_data(block, data); From 4a177406ee869e6e6a067b7796808eca7de7fbb2 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 13 Jun 2023 18:07:55 +0000 Subject: [PATCH 072/113] Inline should_const_prop. --- .../rustc_mir_transform/src/const_prop.rs | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index de9daf45269d..7529ed8186bd 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -541,7 +541,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // type whose creation requires no write. E.g. a generator whose initial state // consists solely of uninitialized memory (so it doesn't capture any locals). let value = self.get_const(place)?; - if !self.should_const_prop(&value) { + if !self.tcx.consider_optimizing(|| format!("ConstantPropagation - {value:?}")) { return None; } trace!("replacing {:?} with {:?}", place, value); @@ -551,10 +551,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let Right(imm) = imm else { return None }; match *imm { - interpret::Immediate::Scalar(scalar) => { + Immediate::Scalar(scalar) if scalar.try_to_int().is_ok() => { Some(self.operand_from_scalar(scalar, value.layout.ty)) } - Immediate::ScalarPair(..) => { + Immediate::ScalarPair(l, r) if l.try_to_int().is_ok() && r.try_to_int().is_ok() => { let alloc = self .ecx .intern_with_temp_alloc(value.layout, |ecx, dest| { @@ -578,21 +578,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - /// Returns `true` if and only if this `op` should be const-propagated into. - fn should_const_prop(&mut self, op: &OpTy<'tcx>) -> bool { - if !self.tcx.consider_optimizing(|| format!("ConstantPropagation - OpTy: {:?}", op)) { - return false; - } - - match **op { - interpret::Operand::Immediate(Immediate::Scalar(s)) => s.try_to_int().is_ok(), - interpret::Operand::Immediate(Immediate::ScalarPair(l, r)) => { - l.try_to_int().is_ok() && r.try_to_int().is_ok() - } - _ => false, - } - } - fn ensure_not_propagated(&self, local: Local) { if cfg!(debug_assertions) { assert!( @@ -744,8 +729,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { ) -> Option> { if let PlaceElem::Index(local) = elem && let Some(value) = self.get_const(local.into()) - && self.should_const_prop(&value) - && let interpret::Operand::Immediate(interpret::Immediate::Scalar(scalar)) = *value + && let interpret::Operand::Immediate(Immediate::Scalar(scalar)) = *value && let Ok(offset) = scalar.to_target_usize(&self.tcx) && let Some(min_length) = offset.checked_add(1) { From 40e116489feabac4289f4a5d65913a28dc36927d Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 20 Jul 2023 23:05:32 +0100 Subject: [PATCH 073/113] Minor improvements to Windows TLS dtors --- .../std/src/sys/windows/thread_local_dtor.rs | 27 +---------- .../std/src/sys/windows/thread_local_key.rs | 46 ++++++++++++++++++- .../src/sys/windows/thread_local_key/tests.rs | 4 ++ 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/library/std/src/sys/windows/thread_local_dtor.rs b/library/std/src/sys/windows/thread_local_dtor.rs index 9707a95dff21..cf542d2bfb83 100644 --- a/library/std/src/sys/windows/thread_local_dtor.rs +++ b/library/std/src/sys/windows/thread_local_dtor.rs @@ -4,29 +4,4 @@ #![unstable(feature = "thread_local_internals", issue = "none")] #![cfg(target_thread_local)] -// Using a per-thread list avoids the problems in synchronizing global state. -#[thread_local] -static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new(); - -// Ensure this can never be inlined because otherwise this may break in dylibs. -// See #44391. -#[inline(never)] -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - DESTRUCTORS.push((t, dtor)); -} - -#[inline(never)] // See comment above -/// Runs destructors. This should not be called until thread exit. -pub unsafe fn run_keyless_dtors() { - // Drop all the destructors. - // - // Note: While this is potentially an infinite loop, it *should* be - // the case that this loop always terminates because we provide the - // guarantee that a TLS key cannot be set after it is flagged for - // destruction. - while let Some((ptr, dtor)) = DESTRUCTORS.pop() { - (dtor)(ptr); - } - // We're done so free the memory. - DESTRUCTORS = Vec::new(); -} +pub use super::thread_local_key::register_keyless_dtor as register_dtor; diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs index 17628b7579b8..036d96596e9c 100644 --- a/library/std/src/sys/windows/thread_local_key.rs +++ b/library/std/src/sys/windows/thread_local_key.rs @@ -1,7 +1,7 @@ use crate::cell::UnsafeCell; use crate::ptr; use crate::sync::atomic::{ - AtomicPtr, AtomicU32, + AtomicBool, AtomicPtr, AtomicU32, Ordering::{AcqRel, Acquire, Relaxed, Release}, }; use crate::sys::c; @@ -9,6 +9,41 @@ use crate::sys::c; #[cfg(test)] mod tests; +/// An optimization hint. The compiler is often smart enough to know if an atomic +/// is never set and can remove dead code based on that fact. +static HAS_DTORS: AtomicBool = AtomicBool::new(false); + +// Using a per-thread list avoids the problems in synchronizing global state. +#[thread_local] +#[cfg(target_thread_local)] +static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new(); + +// Ensure this can never be inlined because otherwise this may break in dylibs. +// See #44391. +#[inline(never)] +#[cfg(target_thread_local)] +pub unsafe fn register_keyless_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + DESTRUCTORS.push((t, dtor)); + HAS_DTORS.store(true, Relaxed); +} + +#[inline(never)] // See comment above +#[cfg(target_thread_local)] +/// Runs destructors. This should not be called until thread exit. +unsafe fn run_keyless_dtors() { + // Drop all the destructors. + // + // Note: While this is potentially an infinite loop, it *should* be + // the case that this loop always terminates because we provide the + // guarantee that a TLS key cannot be set after it is flagged for + // destruction. + while let Some((ptr, dtor)) = DESTRUCTORS.pop() { + (dtor)(ptr); + } + // We're done so free the memory. + DESTRUCTORS = Vec::new(); +} + type Key = c::DWORD; type Dtor = unsafe extern "C" fn(*mut u8); @@ -156,6 +191,8 @@ static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); /// Should only be called once per key, otherwise loops or breaks may occur in /// the linked list. unsafe fn register_dtor(key: &'static StaticKey) { + // Ensure this is never run when native thread locals are available. + assert_eq!(false, cfg!(target_thread_local)); let this = <*const StaticKey>::cast_mut(key); // Use acquire ordering to pass along the changes done by the previously // registered keys when we store the new head with release ordering. @@ -167,6 +204,7 @@ unsafe fn register_dtor(key: &'static StaticKey) { Err(new) => head = new, } } + HAS_DTORS.store(true, Release); } // ------------------------------------------------------------------------- @@ -240,10 +278,14 @@ pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c:: #[allow(dead_code, unused_variables)] unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID) { + if !HAS_DTORS.load(Acquire) { + return; + } if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH { + #[cfg(not(target_thread_local))] run_dtors(); #[cfg(target_thread_local)] - super::thread_local_dtor::run_keyless_dtors(); + run_keyless_dtors(); } // See comments above for what this is doing. Note that we don't need this diff --git a/library/std/src/sys/windows/thread_local_key/tests.rs b/library/std/src/sys/windows/thread_local_key/tests.rs index c95f383fb90e..c739f0caf3ec 100644 --- a/library/std/src/sys/windows/thread_local_key/tests.rs +++ b/library/std/src/sys/windows/thread_local_key/tests.rs @@ -1,3 +1,7 @@ +// This file only tests the thread local key fallback. +// Windows targets with native thread local support do not use this. +#![cfg(not(target_thread_local))] + use super::StaticKey; use crate::ptr; From 715efa418e6a1367e7d2125b02cda77d13a8e44f Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 28 Jun 2023 15:15:22 -0700 Subject: [PATCH 074/113] style-guide: Remove material about tool configurability The style guide discusses the default Rust style. Configurability of Rust formatting tools are not the domain of the style guide. --- src/doc/style-guide/src/README.md | 7 ------ src/doc/style-guide/src/expressions.md | 6 +---- src/doc/style-guide/src/items.md | 32 +------------------------- src/doc/style-guide/src/statements.md | 3 --- 4 files changed, 2 insertions(+), 46 deletions(-) diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md index b8aa64ba14f6..d6af3338ded4 100644 --- a/src/doc/style-guide/src/README.md +++ b/src/doc/style-guide/src/README.md @@ -40,7 +40,6 @@ options. * Each level of indentation must be four spaces (that is, all indentation outside of string literals and comments must be a multiple of four). * The maximum width for a line is 100 characters. -* A tool may choose to make some of these configurable. #### Block indent @@ -99,8 +98,6 @@ fn bar() {} fn baz() {} ``` -Formatting tools may wish to make the bounds on blank lines configurable. - ### [Module-level items](items.md) ### [Statements](statements.md) ### [Expressions](expressions.md) @@ -231,10 +228,6 @@ complexity of an item (for example, that all components must be simple names, not more complex sub-expressions). For more discussion on suitable heuristics, see [this issue](https://github.com/rust-lang-nursery/fmt-rfcs/issues/47). -Tools should give the user an option to ignore such heuristics and always use -the normal formatting. - - ## [Non-formatting conventions](advice.md) ## [Cargo.toml conventions](cargo.md) diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index 41a9eb82cb5a..7e9d71344427 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -814,8 +814,7 @@ let arr = [combinable( )]; ``` -Such behaviour should extend recursively, however, tools may choose to limit the -depth of nesting. +Such behaviour should extend recursively. Only where the multi-line sub-expression is a closure with an explicit block, this combining behaviour may be used where there are other arguments, as long as @@ -852,9 +851,6 @@ compound expression, then use parentheses around it, e.g., `..(x + 1)`, Hexadecimal literals may use upper- or lower-case letters, but they must not be mixed within the same literal. Projects should use the same case for all literals, but we do not make a recommendation for either lower- or upper-case. -Tools should have an option to convert mixed case literals to upper-case, and -may have an option to convert all literals to either lower- or upper-case. - ## Patterns diff --git a/src/doc/style-guide/src/items.md b/src/doc/style-guide/src/items.md index de31e8a8799b..7b5e523d66ef 100644 --- a/src/doc/style-guide/src/items.md +++ b/src/doc/style-guide/src/items.md @@ -15,9 +15,6 @@ alphabetically. When sorting, `self` and `super` must come before any other names. Module declarations should not be moved if they are annotated with `#[macro_use]`, since that may be semantics changing. -Tools should make the above ordering optional. - - ### Function definitions In Rust, people often find functions by searching for `fn [function-name]`, so @@ -372,33 +369,6 @@ where + Index, Output = Self::Output> + Index ``` -#### Option - `where_single_line` - -`where_single_line` is `false` by default. If `true`, then a where clause with -exactly one component may be formatted on a single line if the rest of the -item's signature is also kept on one line. In this case, there is no need for a -trailing comma and if followed by a block, no need for a newline before the -block. E.g., - -```rust -// May be single-lined. -fn foo(args) -> ReturnType -where T: Bound { - body -} - -// Must be multi-lined. -fn foo( - args -) -> ReturnType -where - T: Bound, -{ - body -} -``` - - ### Type aliases Type aliases should generally be kept on one line. If necessary to break the @@ -458,7 +428,7 @@ use a::b::{foo, bar, baz}; #### Large list imports Prefer to use multiple imports rather than a multi-line import. However, tools -should not split imports by default (they may offer this as an option). +should not split imports by default. If an import does require multiple lines (either because a list of single names does not fit within the max width, or because of the rules for nested imports diff --git a/src/doc/style-guide/src/statements.md b/src/doc/style-guide/src/statements.md index a5cd6da10616..7d5d8401318d 100644 --- a/src/doc/style-guide/src/statements.md +++ b/src/doc/style-guide/src/statements.md @@ -120,9 +120,6 @@ following are true: let Some(1) = opt else { return }; ``` -Formatters may allow users to configure the value of the threshold -used to determine whether a let-else statement is *short*. - Otherwise, the let-else statement requires some line breaks. If breaking a let-else statement across multiple lines, never break between the From cf4b20d7cc0e90843e0dc301658b163d1dc6493b Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 28 Jun 2023 18:42:01 -0700 Subject: [PATCH 075/113] style-guide: Fix typo: s/forth/fourth/g --- src/doc/style-guide/src/expressions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index 7e9d71344427..bd3dde68163b 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -608,7 +608,7 @@ match foo { | a_very_long_pattern | another_pattern | yet_another_pattern - | a_forth_pattern => { + | a_fourth_pattern => { ... } } @@ -623,7 +623,7 @@ match foo { a_very_long_pattern | another_pattern | yet_another_pattern - | a_forth_pattern => { + | a_fourth_pattern => { ... } } From 615b58b9f987158e0d264b3f249339a9ebd70ca0 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 3 Jul 2023 16:01:35 -0700 Subject: [PATCH 076/113] style-guide: Fix an example to match the style The style guide requires a trailing comma on where clause components, but then gives an example that doesn't include one. Add the missing trailing comma. --- src/doc/style-guide/src/items.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/style-guide/src/items.md b/src/doc/style-guide/src/items.md index 7b5e523d66ef..ae8a36d68e2f 100644 --- a/src/doc/style-guide/src/items.md +++ b/src/doc/style-guide/src/items.md @@ -366,7 +366,7 @@ where + Index, Output = Self::Output> + Index, Output = Self::Output> + Index, Output = Self::Output> - + Index, Output = Self::Output> + Index + + Index, Output = Self::Output> + Index, ``` ### Type aliases From 081e15a0d81b8feba7e72bb7955543008a89bda0 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 3 Jul 2023 16:14:55 -0700 Subject: [PATCH 077/113] style-guide: Simplify the structure of a recommendation (no semantic change) Avoid putting a sentence fragment after a list; integrate it with the sentence before the list. --- src/doc/style-guide/src/items.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/doc/style-guide/src/items.md b/src/doc/style-guide/src/items.md index ae8a36d68e2f..29a6c6d145c9 100644 --- a/src/doc/style-guide/src/items.md +++ b/src/doc/style-guide/src/items.md @@ -491,14 +491,12 @@ example, `a::*` comes before `b::a` but `a::b` comes before `a::*`. E.g., #### Normalisation -Tools must make the following normalisations: +Tools must make the following normalisations, recursively: * `use a::self;` -> `use a;` * `use a::{};` -> (nothing) * `use a::{b};` -> `use a::b;` -And must apply these recursively. - Tools must not otherwise merge or un-merge import lists or adjust glob imports (without an explicit option). From ce5aca9f5a1726ef892f0feca34ba5454c164857 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 28 Jun 2023 16:56:53 -0700 Subject: [PATCH 078/113] style-guide: Avoid using "should" or "may" for required parts of the default style The style guide inconsistently used language like "there should be a space" or "it should be on its own line", or "may be written on a single line", for things that are required components of the default Rust style. "should" and especially "may" come across as optional. While the style guide overall now has a statement at the top that the default style itself is a *recommendation*, the *definition* of the default style should not be ambiguous about what's part of the default style. Rewrite language in the style guide to only use "should" and "may" and similar for truly optional components of the style (e.g. things a tool cannot or should not enforce in its default configuration). In their place, either use "must", or rewrite in imperative style ("put a space", "start it on the same line"). The latter also substantially reduces the use of passive voice. This is a purely editorial change, and does not affect the semantic definition of the Rust style. --- src/doc/style-guide/src/README.md | 39 ++-- src/doc/style-guide/src/expressions.md | 246 ++++++++++++++----------- src/doc/style-guide/src/items.md | 113 ++++++------ src/doc/style-guide/src/statements.md | 58 +++--- src/doc/style-guide/src/types.md | 16 +- 5 files changed, 252 insertions(+), 220 deletions(-) diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md index d6af3338ded4..29204603e58d 100644 --- a/src/doc/style-guide/src/README.md +++ b/src/doc/style-guide/src/README.md @@ -62,7 +62,8 @@ example) and less rightward drift. ### Trailing commas -Lists should have a trailing comma when followed by a newline: +In comma-separated lists of any kind, use a trailing comma when followed by a +newline: ```rust function_call( @@ -111,17 +112,17 @@ formatter might skip formatting of comments. Prefer line comments (`//`) to block comments (`/* ... */`). -When using line comments there should be a single space after the opening sigil. +When using line comments, put a single space after the opening sigil. -When using single-line block comments there should be a single space after the -opening sigil and before the closing sigil. Multi-line block comments should -have a newline after the opening sigil and before the closing sigil. +When using single-line block comments, put a single space after the opening +sigil and before the closing sigil. For multi-line block comments, put a +newline after the opening sigil, and a newline before the closing sigil. -Prefer to put a comment on its own line. Where a comment follows code, there -should be a single space before it. Where a block comment is inline, there -should be surrounding whitespace as if it were an identifier or keyword. There -should be no trailing whitespace after a comment or at the end of any line in a -multi-line comment. Examples: +Prefer to put a comment on its own line. Where a comment follows code, put a +single space before it. Where a block comment appears inline, use surrounding +whitespace as if it were an identifier or keyword. Do not include trailing +whitespace after a comment or at the end of any line in a multi-line comment. +Examples: ```rust // A comment on an item. @@ -170,7 +171,7 @@ Prefer line comments (`///`) to block comments (`/** ... */`). Prefer outer doc comments (`///` or `/** ... */`), only use inner doc comments (`//!` and `/*! ... */`) to write module-level or crate-level documentation. -Doc comments should come before attributes. +Put doc comments before attributes. ### Attributes @@ -195,18 +196,20 @@ struct CRepr { } ``` -For attributes with an equal sign, there should be a single space before and -after the `=`, e.g., `#[foo = 42]`. +For attributes with an equal sign, put a single space before and after the `=`, +e.g., `#[foo = 42]`. There must only be a single `derive` attribute. Note for tool authors: if combining multiple `derive` attributes into a single attribute, the ordering of -the derived names should be preserved. E.g., `#[derive(bar)] #[derive(foo)] -struct Baz;` should be formatted to `#[derive(bar, foo)] struct Baz;`. +the derived names must generally be preserved for correctness: +`#[derive(Foo)] #[derive(Bar)] struct Baz;` must be formatted to +`#[derive(Foo, Bar)] struct Baz;`. ### *small* items -In many places in this guide we specify that a formatter may format an item -differently if it is *small*, for example struct literals: +In many places in this guide we specify formatting that depends on a code +construct being *small*. For example, single-line vs multi-line struct +literals: ```rust // Normal formatting @@ -215,7 +218,7 @@ Foo { f2: another_expression(), } -// *small* formatting +// "small" formatting Foo { f1, f2 } ``` diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index bd3dde68163b..fec6ae5acf3d 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -2,10 +2,13 @@ ### Blocks -A block expression should have a newline after the initial `{` and before the -terminal `}`. Any qualifier before the block (e.g., `unsafe`) should always be -on the same line as the opening brace, and separated with a single space. The -contents of the block should be block indented: +A block expression must have a newline after the initial `{` and before the +terminal `}`, unless it qualifies to be written as a single line based on +another style rule. + +A keyword before the block (such as `unsafe` or `async`) must be on the same +line as the opening brace, with a single space between the keyword and the +opening brace. Indent the contents of the block. ```rust fn block_as_stmt() { @@ -40,7 +43,7 @@ fn unsafe_block_as_stmt() { } ``` -If a block has an attribute, it should be on its own line: +If a block has an attribute, put it on its own line before the block: ```rust fn block_as_stmt() { @@ -54,18 +57,18 @@ fn block_as_stmt() { } ``` -Avoid writing comments on the same line as the braces. +Avoid writing comments on the same lines as either of the braces. -An empty block should be written as `{}`. +Write an empty block as `{}`. -A block may be written on a single line if: +Write a block on a single line if: * it is either used in expression position (not statement position) or is an unsafe block in statement position -* contains a single-line expression and no statements -* contains no comments +* it contains a single-line expression and no statements +* it contains no comments -A single line block should have spaces after the opening brace and before the +For a single-line block, put spaces after the opening brace and before the closing brace. Examples: @@ -117,9 +120,9 @@ fn main() { ### Closures Don't put any extra spaces before the first `|` (unless the closure is prefixed -by `move`); put a space between the second `|` and the expression of the -closure. Between the `|`s, you should use function definition syntax, however, -elide types where possible. +by a keyword such as `move`); put a space between the second `|` and the +expression of the closure. Between the `|`s, use function definition syntax, +but elide types where possible. Use closures without the enclosing `{}`, if possible. Add the `{}` when you have a return type, when there are statements, there are comments in the body, or the @@ -155,13 +158,14 @@ move |arg1: i32, arg2: i32| -> i32 { ### Struct literals -If a struct literal is *small* it may be formatted on a single line. If not, -each field should be on it's own, block-indented line. There should be a -trailing comma in the multi-line form only. There should be a space after the -colon only. +If a struct literal is *small*, format it on a single line, and do not use a +trailing comma. If not, split it across multiple lines, with each field on its +own block-indented line, and use a trailing comma. -There should be a space before the opening brace. In the single-line form there -should be spaces after the opening brace and before the closing brace. +For each `field: value` entry, put a space after the colon only. + +Put a space before the opening brace. In the single-line form, put spaces after +the opening brace and before the closing brace. ```rust Foo { field1, field2: 0 } @@ -172,7 +176,7 @@ let f = Foo { ``` Functional record update syntax is treated like a field, but it must never have -a trailing comma. There should be no space after `..`. +a trailing comma. Do not put a space after `..`. let f = Foo { field1, @@ -182,9 +186,13 @@ let f = Foo { ### Tuple literals -Use a single-line form where possible. There should not be spaces around the -parentheses. Where a single-line form is not possible, each element of the tuple -should be on its own block-indented line and there should be a trailing comma. +Use a single-line form where possible. Do not put spaces between the opening +parenthesis and the first element, or between the last element and the closing +parenthesis. Separate elements with a comma followed by a space. + +Where a single-line form is not possible, write the tuple across +multiple lines, with each element of the tuple on its own block-indented line, +and use a trailing comma. ```rust (a, b, c) @@ -198,14 +206,23 @@ let x = ( ### Tuple struct literals -There should be no space between the identifier and the opening parenthesis. -Otherwise, follow the rules for tuple literals, e.g., `Foo(a, b)`. +Do not put space between the identifier and the opening parenthesis. Otherwise, +follow the rules for tuple literals: + +```rust +Foo(a, b, c) + +let x = Foo( + a_long_expr, + another_very_long_expr, +); +``` ### Enum literals Follow the formatting rules for the various struct literals. Prefer using the -name of the enum as a qualifying name, unless the enum is in the prelude. E.g., +name of the enum as a qualifying name, unless the enum is in the prelude: ```rust Foo::Bar(a, b) @@ -219,24 +236,29 @@ Ok(an_expr) ### Array literals -For simple array literals, avoid line breaking, no spaces around square -brackets, contents of the array should be separated by commas and spaces. If -using the repeating initialiser, there should be a space after the semicolon -only. Apply the same rules if using the `vec!` or similar macros (always use -square brackets here). Examples: +Write small array literals on a single line. Do not put spaces between the opening +square bracket and the first element, or between the last element and the closing +square bracket. Separate elements with a comma followed by a space. + +If using the repeating initializer, put a space after the semicolon +only. + +Apply the same rules if using `vec!` or similar array-like macros; always use +square brackets with such macros. Examples: ```rust fn main() { - [1, 2, 3]; - vec![a, b, c, d]; + let x = [1, 2, 3]; + let y = vec![a, b, c, d]; let a = [42; 10]; } ``` -If a line must be broken, prefer breaking only after the `;`, if possible. -Otherwise, follow the rules below for function calls. In any case, the contents -of the initialiser should be block indented and there should be line breaks -after the opening bracket and before the closing bracket: +For arrays large enough to break across lines, if using the repeating +initializer, break after the `;`, not before. Otherwise, follow the rules below +for function calls. In any case, block-indent the contents of the initializer, +and put line breaks after the opening square bracket and before the closing +square bracket: ```rust fn main() { @@ -255,11 +277,12 @@ fn main() { ### Array accesses, indexing, and slicing. -No spaces around the square brackets, avoid breaking lines if possible, never -break a line between the target expression and the opening bracket. If the -indexing expression covers multiple lines, then it should be block indented and -there should be newlines after the opening brackets and before the closing -bracket. However, this should be avoided where possible. +Don't put spaces around the square brackets. Avoid breaking lines if possible. +Never break a line between the target expression and the opening square +bracket. If the indexing expression must be broken onto a subsequent line, or +spans multiple lines itself, then block-indent the indexing expression, and put +newlines after the opening square bracket and before the closing square +bracket: Examples: @@ -291,7 +314,7 @@ if you have `t: &T`, and `u: U`, prefer `*t op u` to `t op &u`. In general, within expressions, prefer dereferencing to taking references, unless necessary (e.g. to avoid an unnecessarily expensive operation). -Use parentheses liberally, do not necessarily elide them due to precedence. +Use parentheses liberally; do not necessarily elide them due to precedence. Tools should not automatically insert or remove parentheses. Do not use spaces to indicate precedence. @@ -353,10 +376,10 @@ foo(x, y, z) #### Multi-line calls If the function call is not *small*, it would otherwise over-run the max width, -or any argument or the callee is multi-line, then the call should be formatted -across multiple lines. In this case, each argument should be on it's own block- -indented line, there should be a newline after the opening parenthesis and -before the closing parenthesis, and there should be a trailing comma. E.g., +or any argument or the callee is multi-line, then format the call across +multiple lines. In this case, put each argument on its own block-indented line, +break after the opening parenthesis and before the closing parenthesis, +and use a trailing comma: ```rust a_function_call( @@ -379,17 +402,18 @@ x.foo().bar().baz(x, y, z); ### Macro uses -Macros which can be parsed like other constructs should be formatted like those +If a macro can be parsed like other constructs, format it like those constructs. For example, a macro use `foo!(a, b, c)` can be parsed like a -function call (ignoring the `!`), therefore it should be formatted following the -rules for function calls. +function call (ignoring the `!`), so format it using the rules for function +calls. #### Special case macros -Macros which take a format string and where all other arguments are *small* may -be formatted with arguments before and after the format string on a single line -and the format string on its own line, rather than putting each argument on its -own line. For example, +For macros which take a format string, if all other arguments are *small*, +format the arguments before the format string on a single line if they fit, and +format the arguments after the format string on a single line if they fit, with +the format string on its own line. If the arguments are not small or do not +fit, put each on its own line as with a function. For example: ```rust println!( @@ -416,13 +440,13 @@ let cstr = "Hi\0" as *const str as *const [u8] as *const std::os::raw::c_char; ### Chains of fields and method calls -A chain is a sequence of field accesses and/or method calls. A chain may also -include the try operator ('?'). E.g., `a.b.c().d` or `foo?.bar().baz?`. +A chain is a sequence of field accesses, method calls, and/or uses of the try +operator `?`. E.g., `a.b.c().d` or `foo?.bar().baz?`. -Prefer formatting on one line if possible, and the chain is *small*. If -formatting on multiple lines, each field access or method call in the chain -should be on its own line with the line-break before the `.` and after any `?`. -Each line should be block-indented. E.g., +Format the chain on one line if it is "small" and otherwise possible to do so. +If formatting on multiple lines, put each field access or method call in the +chain on its own line, with the line-break before the `.` and after any `?`. +Block-indent each subsequent line: ```rust let foo = bar @@ -431,8 +455,8 @@ let foo = bar ``` If the length of the last line of the first element plus its indentation is -less than or equal to the indentation of the second line (and there is space), -then combine the first and second lines, e.g., +less than or equal to the indentation of the second line, then combine the +first and second lines if they fit: ```rust x.baz? @@ -489,13 +513,13 @@ self.pre_comment.as_ref().map_or( This section covers `if`, `if let`, `loop`, `while`, `while let`, and `for` expressions. -The keyword, any initial clauses, and the opening brace of the block should be -on a single line. The usual rules for [block formatting](#blocks) should be -applied to the block. +Put the keyword, any initial clauses, and the opening brace of the block all on +a single line, if they fit. Apply the usual rules for [block +formatting](#blocks) to the block. -If there is an `else` component, then the closing brace, `else`, any following -clause, and the opening brace should all be on the same line. There should be a -single space before and after the `else` keyword. For example: +If there is an `else` component, then put the closing brace, `else`, any +following clause, and the opening brace all on the same line, with a single +space before and after the `else` keyword: ```rust if ... { @@ -513,10 +537,10 @@ if let ... { } ``` -If the control line needs to be broken, then prefer to break before the `=` in -`* let` expressions and before `in` in a `for` expression; the following line -should be block indented. If the control line is broken for any reason, then the -opening brace should be on its own line and not indented. Examples: +If the control line needs to be broken, prefer to break before the `=` in `* +let` expressions and before `in` in a `for` expression; block-indent the +following line. If the control line is broken for any reason, put the opening +brace on its own line, not indented. Examples: ```rust while let Some(foo) @@ -539,10 +563,10 @@ if a_long_expression } ``` -Where the initial clause is multi-lined and ends with one or more closing -parentheses, square brackets, or braces, and there is nothing else on that line, -and that line is not indented beyond the indent on the first line of the control -flow expression, then the opening brace of the block should be put on the same +Where the initial clause spans multiple lines and ends with one or more closing +parentheses, square brackets, or braces, and there is nothing else on that +line, and that line is not indented beyond the indent on the first line of the +control flow expression, then put the opening brace of the block on the same line with a preceding space. For example: ```rust @@ -558,9 +582,9 @@ if !self.config.file_lines().intersects( #### Single line `if else` -Formatters may place an `if else` or `if let else` on a single line if it occurs -in expression context (i.e., is not a standalone statement), it contains a -single `else` clause, and is *small*. For example: +Put an `if else` or `if let else` on a single line if it occurs in expression +context (i.e., is not a standalone statement), it contains a single `else` +clause, and is *small*: ```rust let y = if x { 0 } else { 1 }; @@ -582,9 +606,9 @@ if x { ### Match -Prefer not to line-break inside the discriminant expression. There must always -be a line break after the opening brace and before the closing brace. The match -arms must be block indented once: +Prefer not to line-break inside the discriminant expression. Always break after +the opening brace and before the closing brace. Block-indent the match arms +once: ```rust match foo { @@ -598,7 +622,7 @@ let x = match foo.bar.baz() { Use a trailing comma for a match arm if and only if not using a block. -Never start a match arm pattern with `|`, e.g., +Never start a match arm pattern with `|`: ```rust match foo { @@ -614,8 +638,7 @@ match foo { } ``` -Prefer - +Prefer: ```rust match foo { @@ -633,11 +656,11 @@ Avoid splitting the left-hand side (before the `=>`) of a match arm where possible. If the right-hand side of the match arm is kept on the same line, never use a block (unless the block is empty). -If the right-hand side consists of multiple statements or has line comments or -the start of the line cannot be fit on the same line as the left-hand side, use -a block. +If the right-hand side consists of multiple statements, or has line comments, +or the start of the line does not fit on the same line as the left-hand side, +use a block. -The body of a block arm should be block indented once. +Block-indent the body of a block arm. Examples: @@ -662,8 +685,8 @@ match foo { ``` If the body is a single expression with no line comments and not a control flow -expression, then it may be started on the same line as the right-hand side. If -not, then it must be in a block. Example, +expression, start it on the same line as the right-hand side. If not, then it +must be in a block. Example: ```rust match foo { @@ -687,8 +710,8 @@ match foo { #### Line-breaking -Where it is possible to use a block form on the right-hand side and avoid -breaking the left-hand side, do that. E.g. +If using a block form on the right-hand side of a match arm makes it possible +to avoid breaking on the left-hand side, do that: ```rust // Assuming the following line does not fit in the max width @@ -720,7 +743,7 @@ body on a new line: If required to break the pattern, put each clause of the pattern on its own line with no additional indent, breaking before the `|`. If there is an `if` -clause, then you must use the above form: +clause, use the above form: ```rust a_very_long_pattern @@ -740,7 +763,7 @@ clause, then you must use the above form: ``` If the pattern is multi-line, and the last line is less wide than the indent, do -not put the `if` clause on a newline. E.g., +not put the `if` clause on a new line. E.g., ```rust Token::Dimension { @@ -753,8 +776,8 @@ not put the `if` clause on a newline. E.g., ``` If every clause in a pattern is *small*, but the whole pattern does not fit on -one line, then the pattern may be formatted across multiple lines with as many -clauses per line as possible. Again break before a `|`: +one line, then format the pattern across multiple lines with as many clauses +per line as possible. Again, break before a `|`: ```rust foo | bar | baz @@ -783,8 +806,8 @@ E.g., `&&Some(foo)` matches, `Foo(4, Bar)` does not. ### Combinable expressions Where a function call has a single argument, and that argument is formatted -across multiple-lines, the outer call may be formatted as if it were a single- -line call. The same combining behaviour may be applied to any similar +across multiple-lines, format the outer call as if it were a single-line call, +if the result fits. Apply the same combining behaviour to any similar expressions which have multi-line, block-indented lists of sub-expressions delimited by parentheses (e.g., macros or tuple struct literals). E.g., @@ -814,12 +837,12 @@ let arr = [combinable( )]; ``` -Such behaviour should extend recursively. +Apply this behavior recursively. -Only where the multi-line sub-expression is a closure with an explicit block, -this combining behaviour may be used where there are other arguments, as long as -all the arguments and the first line of the closure fit on the first line, the -closure is the last argument, and there is only one closure argument: +For a function with multiple arguments, if the last argument is a multi-line +closure with an explicit block, there are no other closure arguments, and all +the arguments and the first line of the closure fit on the first line, use the +same combining behavior: ```rust foo(first_arg, x, |param| { @@ -834,16 +857,17 @@ foo(first_arg, x, |param| { Do not put spaces in ranges, e.g., `0..10`, `x..=y`, `..x.len()`, `foo..`. When writing a range with both upper and lower bounds, if the line must be -broken, break before the range operator and block indent the second line: +broken within the range, break before the range operator and block indent the +second line: ```rust a_long_expression ..another_long_expression ``` -For the sake of indicating precedence, we recommend that if either bound is a -compound expression, then use parentheses around it, e.g., `..(x + 1)`, -`(x.f)..(x.f.len())`, or `0..(x - 10)`. +For the sake of indicating precedence, if either bound is a compound +expression, use parentheses around it, e.g., `..(x + 1)`, `(x.f)..(x.f.len())`, +or `0..(x - 10)`. ### Hexadecimal literals @@ -854,5 +878,5 @@ literals, but we do not make a recommendation for either lower- or upper-case. ## Patterns -Patterns should be formatted like their corresponding expressions. See the -section on `match` for additional formatting for patterns in match arms. +Format patterns like their corresponding expressions. See the section on +`match` for additional formatting for patterns in match arms. diff --git a/src/doc/style-guide/src/items.md b/src/doc/style-guide/src/items.md index 29a6c6d145c9..1e7a88065b5e 100644 --- a/src/doc/style-guide/src/items.md +++ b/src/doc/style-guide/src/items.md @@ -9,16 +9,17 @@ an item appears at module level or within another item. alphabetically. `use` statements, and module *declarations* (`mod foo;`, not `mod { ... }`) -must come before other items. We recommend that imports come before module -declarations; if imports and modules are separated, then they should be ordered -alphabetically. When sorting, `self` and `super` must come before any other -names. Module declarations should not be moved if they are annotated with -`#[macro_use]`, since that may be semantics changing. +must come before other items. Put imports before module declarations. Sort each +alphabetically, except that `self` and `super` must come before any other +names. + +Don't automatically move module declarations annotated with `#[macro_use]`, +since that might change semantics. ### Function definitions In Rust, people often find functions by searching for `fn [function-name]`, so -the formatting of function definitions shold enable this. +the formatting of function definitions must enable this. The proper ordering and spacing is: @@ -80,9 +81,9 @@ enum FooBar { } ``` -If a struct variant is [*small*](index.html#small-items), it may be formatted on -one line. In this case, do not use a trailing comma for the field list, but do -put spaces around each brace: +If a struct variant is [*small*](index.html#small-items), format it on one +line. In this case, do not use a trailing comma for the field list, but do put +spaces around each brace: ```rust enum FooBar { @@ -91,9 +92,9 @@ enum FooBar { ``` In an enum with multiple struct variants, if any struct variant is written on -multiple lines, then the multi-line formatting should be used for all struct -variants. However, such a situation might be an indication that you should -factor out the fields of the variant into their own struct. +multiple lines, use the multi-line formatting for all struct variants. However, +such a situation might be an indication that you should factor out the fields +of the variant into their own struct. ### Structs and Unions @@ -140,9 +141,9 @@ union Foo { ### Tuple structs -Put the whole struct on one line if possible. Types in the parentheses should be -separated by a comma and space with no trailing comma. No spaces around the -parentheses or semicolon: +Put the whole struct on one line if possible. Separate types within the +parentheses using a comma and space. Don't use a trailing comma for a +single-line tuple struct. Don't put spaces around the parentheses or semicolon: ```rust pub struct Foo(String, u8); @@ -151,9 +152,11 @@ pub struct Foo(String, u8); Prefer unit structs to empty tuple structs (these only exist to simplify code generation), e.g., `struct Foo;` rather than `struct Foo();`. -For more than a few fields, prefer a proper struct with named fields. Given -this, a tuple struct should always fit on one line. If it does not, block format -the fields with a field on each line and a trailing comma: +For more than a few fields (in particular if the tuple struct does not fit on +one line), prefer a proper struct with named fields. + +For a multi-line tuple struct, block-format the fields with a field on each +line and a trailing comma: ```rust pub struct Foo( @@ -165,9 +168,9 @@ pub struct Foo( ### Traits -Trait items should be block-indented. If there are no items, the trait may be -formatted on a single line. Otherwise there should be line-breaks after the -opening brace and before the closing brace: +Use block-indent for trait items. If there are no items, format the trait (including its `{}`) +on a single line. Otherwise, break after the opening brace and before +the closing brace: ```rust trait Foo {} @@ -177,8 +180,8 @@ pub trait Bar { } ``` -If the trait has bounds, there should be a space after the colon but not before -and before and after each `+`, e.g., +If the trait has bounds, put a space after the colon but not before, +and put spaces around each `+`, e.g., ```rust trait Foo: Debug + Bar {} @@ -204,9 +207,9 @@ pub trait IndexRanges: ### Impls -Impl items should be block indented. If there are no items, the impl may be -formatted on a single line. Otherwise there should be line-breaks after the -opening brace and before the closing brace: +Use block-indent for impl items. If there are no items, format the impl +(including its `{}`) on a single line. Otherwise, break after the opening brace +and before the closing brace: ```rust impl Foo {} @@ -264,12 +267,12 @@ macro_rules! foo { Prefer to put a generics clause on one line. Break other parts of an item declaration rather than line-breaking a generics clause. If a generics clause is -large enough to require line-breaking, you should prefer to use a `where` clause -instead. +large enough to require line-breaking, prefer a `where` clause instead. -Do not put spaces before or after `<` nor before `>`. Only put a space after `>` -if it is followed by a word or opening brace, not an opening parenthesis. There -should be a space after each comma and no trailing comma. +Do not put spaces before or after `<` nor before `>`. Only put a space after +`>` if it is followed by a word or opening brace, not an opening parenthesis. +Put a space after each comma. Do not use a trailing comma for a single-line +generics clause. ```rust fn foo(x: Vec, y: Vec) ... @@ -277,10 +280,9 @@ fn foo(x: Vec, y: Vec) ... impl SomeType { ... ``` -If the generics clause must be formatted across multiple lines, each parameter -should have its own block-indented line, there should be newlines after the -opening bracket and before the closing bracket, and there should be a trailing -comma. +If the generics clause must be formatted across multiple lines, put each +parameter on its own block-indented line, break after the opening `<` and +before the closing `>`, and use a trailing comma. ```rust fn foo< @@ -289,8 +291,7 @@ fn foo< >(x: Vec, y: Vec) ... ``` -If an associated type is bound in a generic type, then there should be spaces on -either side of the `=`: +If an associated type is bound in a generic type, put spaces around the `=`: ```rust > @@ -303,12 +304,14 @@ Prefer to use single-letter names for generic parameters. These rules apply for `where` clauses on any item. -A `where` clause may immediately follow a closing bracket of any kind. -Otherwise, it must start a new line, with no indent. Each component of a `where` -clause must be on its own line and be block indented. There should be a trailing +If immediately following a closing bracket of any kind, write the keyword +`where` on the same line, with a space before it. + +Otherwise, put `where` on a new line at the same indentation level. Put each +component of a `where` clause on its own line, block-indented. Use a trailing comma, unless the clause is terminated with a semicolon. If the `where` clause -is followed by a block (or assignment), the block should be started on a new -line. Examples: +is followed by a block (or assignment), start that block on a new line. +Examples: ```rust fn function(args) @@ -352,12 +355,12 @@ where = Bar; ``` -If a `where` clause is very short, we recommend using an inline bound on the -type parameter. +If a `where` clause is very short, prefer using an inline bound on the type +parameter. - -If a component of a `where` clause is long, it may be broken before `+` and -further block indented. Each bound should go on its own line. E.g., +If a component of a `where` clause does not fit and contains `+`, break it +before each `+` and block-indent the continuation lines. Put each bound on its +own line. E.g., ```rust impl IndexRanges for T @@ -371,8 +374,8 @@ where ### Type aliases -Type aliases should generally be kept on one line. If necessary to break the -line, do so after the `=`; the right-hand-side should be block indented: +Keep type aliases on one line when they fit. If necessary to break the line, do +so after the `=`, and block-indent the right-hand side: ```rust pub type Foo = Bar; @@ -397,9 +400,8 @@ where ### Associated types -Associated types should follow the guidelines above for type aliases. Where an -associated type has a bound, there should be a space after the colon but not -before: +Format associated types like type aliases. Where an associated type has a +bound, put a space after the colon but not before: ```rust pub type Foo: Bar; @@ -408,15 +410,14 @@ pub type Foo: Bar; ### extern items -When writing extern items (such as `extern "C" fn`), always be explicit about -the ABI. For example, write `extern "C" fn foo ...`, not `extern fn foo ...`, or +When writing extern items (such as `extern "C" fn`), always specify the ABI. +For example, write `extern "C" fn foo ...`, not `extern fn foo ...`, or `extern "C" { ... }`. ### Imports (`use` statements) -If an import can be formatted on one line, do so. There should be no spaces -around braces. +Format imports on one line where possible. Don't put spaces around braces. ```rust use a::b::c; diff --git a/src/doc/style-guide/src/statements.md b/src/doc/style-guide/src/statements.md index 7d5d8401318d..8c8f893fbe90 100644 --- a/src/doc/style-guide/src/statements.md +++ b/src/doc/style-guide/src/statements.md @@ -2,8 +2,8 @@ ### Let statements -There should be spaces after the `:` and on both sides of the `=` (if they are -present). No space before the semicolon. +Put a space after the `:` and on both sides of the `=` (if they are present). +Don't put a space before the semicolon. ```rust // A comment. @@ -14,19 +14,19 @@ let pattern: Type; let pattern = expr; ``` -If possible the declaration should be formatted on a single line. If this is not -possible, then try splitting after the `=`, if the declaration can fit on two -lines. The expression should be block indented. +If possible, format the declaration on a single line. If not possible, then try +splitting after the `=`, if the declaration fits on two lines. Block-indent the +expression. ```rust let pattern: Type = expr; ``` -If the first line does not fit on a single line, then split after the colon, -using block indentation. If the type covers multiple lines, even after line- -breaking after the `:`, then the first line may be placed on the same line as -the `:`, subject to the [combining rules](https://github.com/rust-lang-nursery/fmt-rfcs/issues/61) (WIP). +If the first line still does not fit on a single line, split after the `:`, and +use block indentation. If the type requires multiple lines, even after +line-breaking after the `:`, then place the first line on the same line as the +`:`, subject to the [combining rules](expressions.html#combinable-expressions). ```rust @@ -51,12 +51,12 @@ let (abcd, ``` If the expression covers multiple lines, if the first line of the expression -fits in the remaining space, it stays on the same line as the `=`, the rest of the -expression is not indented. If the first line does not fit, then it should start -on the next lines, and should be block indented. If the expression is a block -and the type or pattern cover multiple lines, then the opening brace should be -on a new line and not indented (this provides separation for the interior of the -block from the type), otherwise the opening brace follows the `=`. +fits in the remaining space, it stays on the same line as the `=`, and the rest +of the expression is not further indented. If the first line does not fit, then +put the expression on subsequent lines, block indented. If the expression is a +block and the type or pattern cover multiple lines, put the opening brace on a +new line and not indented (this provides separation for the interior of the +block from the type); otherwise, the opening brace follows the `=`. Examples: @@ -108,8 +108,8 @@ In this case, always apply the same formatting rules to the components preceding the `else` block (i.e. the `let pattern: Type = initializer_expr` portion) as described [for other let statements](#let-statements). -The entire let-else statement may be formatted on a single line if all the -following are true: +Format the entire let-else statement on a single line if all the following are +true: * the entire statement is *short* * the `else` block contains only a single-line expression and no statements @@ -154,9 +154,9 @@ before the `else`. }; ``` -If the initializer expression is multi-line, the `else` keyword and opening -brace of the block (i.e. `else {`) should be put on the same line as the end of -the initializer expression, with a space between them, if and only if all the +If the initializer expression is multi-line, put the `else` keyword and opening +brace of the block (i.e. `else {`) on the same line as the end of the +initializer expression, with a space between them, if and only if all the following are true: * The initializer expression ends with one or more closing @@ -179,9 +179,9 @@ let Some(x) = y.foo( } ``` -Otherwise, the `else` keyword and opening brace should be placed on the next -line after the end of the initializer expression, and the `else` keyword should -have the same indentation level as the `let` keyword. +Otherwise, put the `else` keyword and opening brace on the next line after the +end of the initializer expression, with the `else` keyword at the same +indentation level as the `let` keyword. For example: @@ -233,9 +233,9 @@ fn main() { ### Macros in statement position -A macro use in statement position should use parentheses or square brackets as -delimiters and should be terminated with a semicolon. There should be no spaces -between the name, `!`, the delimiters, or the `;`. +For a macro use in statement position, use parentheses or square brackets as +delimiters, and terminate it with a semicolon. Do not put spaces around the +name, `!`, the delimiters, or the `;`. ```rust // A comment. @@ -245,14 +245,14 @@ a_macro!(...); ### Expressions in statement position -There should be no space between the expression and the semicolon. +Do not put space between the expression and the semicolon. ``` ; ``` -All expressions in statement position should be terminated with a semicolon, -unless they end with a block or are used as the value for a block. +Terminate all expressions in statement position with a semicolon, unless they +end with a block or are used as the value for a block. E.g., diff --git a/src/doc/style-guide/src/types.md b/src/doc/style-guide/src/types.md index ae456ef21c8d..15b001d4f2ea 100644 --- a/src/doc/style-guide/src/types.md +++ b/src/doc/style-guide/src/types.md @@ -7,14 +7,14 @@ * `*const T`, `*mut T` (no space after `*`, space before type) * `&'a T`, `&T`, `&'a mut T`, `&mut T` (no space after `&`, single spaces separating other words) * `unsafe extern "C" fn<'a, 'b, 'c>(T, U, V) -> W` or `fn()` (single spaces around keywords and sigils, and after commas, no trailing commas, no spaces around brackets) -* `!` should be treated like any other type name, `Name` +* `!` gets treated like any other type name, `Name` * `(A, B, C, D)` (spaces after commas, no spaces around parens, no trailing comma unless it is a one-tuple) * ` as SomeTrait>::Foo::Bar` or `Foo::Bar` or `::Foo::Bar` (no spaces around `::` or angle brackets, single spaces around `as`) * `Foo::Bar` (spaces after commas, no trailing comma, no spaces around angle brackets) * `T + T + T` (single spaces between types, and `+`). * `impl T + T + T` (single spaces between keyword, types, and `+`). -Parentheses used in types should not be surrounded by whitespace, e.g., `(Foo)` +Do not put space around parentheses used in types, e.g., `(Foo)` ### Line breaks @@ -37,13 +37,17 @@ Foo> ``` -`[T; expr]` may be broken after the `;` if necessary. +If a type requires line-breaks in order to fit, this section outlines where to +break such types if necessary. -Function types may be broken following the rules for function declarations. +Break `[T; expr]` after the `;` if necessary. -Generic types may be broken following the rules for generics. +Break function types following the rules for function declarations. -Types with `+` may be broken after any `+` using block indent and breaking before the `+`. When breaking such a type, all `+`s should be line broken, e.g., +Break generic types following the rules for generics. + +Break types with `+` by breaking before the `+` and block-indenting the +subsequent lines. When breaking such a type, break before *every* `+`: ```rust impl Clone From 9ccc104d14a93c593b8e4e3982037623a6fe5e0d Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 5 Jul 2023 15:21:56 -0700 Subject: [PATCH 079/113] style-guide: Add an additional chaining example Make it clear the rule for stacking the second line on the first applies recursively, as long as the condition holds. --- src/doc/style-guide/src/expressions.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index fec6ae5acf3d..633fdc909bd0 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -456,12 +456,15 @@ let foo = bar If the length of the last line of the first element plus its indentation is less than or equal to the indentation of the second line, then combine the -first and second lines if they fit: +first and second lines if they fit. Apply this rule recursively. ```rust x.baz? .qux() +x.y.z + .qux() + let foo = x .baz? .qux(); From 69d29a70daa5e8df79d2016af681b13c82f561df Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 5 Jul 2023 16:33:21 -0700 Subject: [PATCH 080/113] style-guide: Fix typo: s/right-hand side/left-hand side/ --- src/doc/style-guide/src/expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index 633fdc909bd0..42ecc442eb36 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -688,7 +688,7 @@ match foo { ``` If the body is a single expression with no line comments and not a control flow -expression, start it on the same line as the right-hand side. If not, then it +expression, start it on the same line as the left-hand side. If not, then it must be in a block. Example: ```rust From 144e8a38669c874464624e4d27f418eb02f29eff Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 5 Jul 2023 16:36:47 -0700 Subject: [PATCH 081/113] style-guide: Fix example to match the rule it exemplifies (and match rustfmt) An example immediately following "Put each bound on its own line." did not put each bound on its own line. --- src/doc/style-guide/src/items.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/doc/style-guide/src/items.md b/src/doc/style-guide/src/items.md index 1e7a88065b5e..6e5ea335e6ab 100644 --- a/src/doc/style-guide/src/items.md +++ b/src/doc/style-guide/src/items.md @@ -369,7 +369,8 @@ where + Index, Output = Self::Output> + Index, Output = Self::Output> + Index, Output = Self::Output> - + Index, Output = Self::Output> + Index, + + Index, Output = Self::Output> + + Index, ``` ### Type aliases From 77d09cb69e7e459b6eee9d7564844c73064b2e84 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 20 Jul 2023 13:51:46 -0700 Subject: [PATCH 082/113] Clarify wording on breaking arrays across lines Co-authored-by: Caleb Cartwright --- src/doc/style-guide/src/expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index 42ecc442eb36..0aed8763a062 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -254,7 +254,7 @@ fn main() { } ``` -For arrays large enough to break across lines, if using the repeating +For arrays that have to be broken across lines, if using the repeating initializer, break after the `;`, not before. Otherwise, follow the rules below for function calls. In any case, block-indent the contents of the initializer, and put line breaks after the opening square bracket and before the closing From cbabd00c12f83ec5ea963d1686bb99342ba28607 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 20 Jul 2023 22:20:53 -0300 Subject: [PATCH 083/113] Add tables to Stable::stable --- compiler/rustc_smir/src/rustc_smir/mod.rs | 208 ++++++++++++---------- 1 file changed, 111 insertions(+), 97 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index f512a98f41a4..8999d44133dd 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -49,8 +49,12 @@ impl<'tcx> Context for Tables<'tcx> { .basic_blocks .iter() .map(|block| stable_mir::mir::BasicBlock { - terminator: block.terminator().stable(), - statements: block.statements.iter().map(mir::Statement::stable).collect(), + terminator: block.terminator().stable(self), + statements: block + .statements + .iter() + .map(|statement| statement.stable(self)) + .collect(), }) .collect(), locals: mir.local_decls.iter().map(|decl| self.intern_ty(decl.ty)).collect(), @@ -110,11 +114,13 @@ impl<'tcx> Tables<'tcx> { } ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(self.intern_ty(*ty))), ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { - TyKind::RigidTy(RigidTy::RawPtr(self.intern_ty(*ty), mutbl.stable())) - } - ty::Ref(region, ty, mutbl) => { - TyKind::RigidTy(RigidTy::Ref(opaque(region), self.intern_ty(*ty), mutbl.stable())) + TyKind::RigidTy(RigidTy::RawPtr(self.intern_ty(*ty), mutbl.stable(self))) } + ty::Ref(region, ty, mutbl) => TyKind::RigidTy(RigidTy::Ref( + opaque(region), + self.intern_ty(*ty), + mutbl.stable(self), + )), ty::FnDef(def_id, generic_args) => TyKind::RigidTy(RigidTy::FnDef( rustc_internal::fn_def(*def_id), self.generic_args(generic_args), @@ -187,20 +193,20 @@ fn smir_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> stable_mir::Crate { } /// Trait used to convert between an internal MIR type to a Stable MIR type. -pub(crate) trait Stable { +pub(crate) trait Stable<'tcx> { /// The stable representation of the type implementing Stable. type T; /// Converts an object to the equivalent Stable MIR representation. - fn stable(&self) -> Self::T; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T; } -impl<'tcx> Stable for mir::Statement<'tcx> { +impl<'tcx> Stable<'tcx> for mir::Statement<'tcx> { type T = stable_mir::mir::Statement; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use rustc_middle::mir::StatementKind::*; match &self.kind { Assign(assign) => { - stable_mir::mir::Statement::Assign(assign.0.stable(), assign.1.stable()) + stable_mir::mir::Statement::Assign(assign.0.stable(tables), assign.1.stable(tables)) } FakeRead(_) => todo!(), SetDiscriminant { .. } => todo!(), @@ -218,45 +224,51 @@ impl<'tcx> Stable for mir::Statement<'tcx> { } } -impl<'tcx> Stable for mir::Rvalue<'tcx> { +impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { type T = stable_mir::mir::Rvalue; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use mir::Rvalue::*; match self { - Use(op) => stable_mir::mir::Rvalue::Use(op.stable()), + Use(op) => stable_mir::mir::Rvalue::Use(op.stable(tables)), Repeat(_, _) => todo!(), - Ref(region, kind, place) => { - stable_mir::mir::Rvalue::Ref(opaque(region), kind.stable(), place.stable()) - } + Ref(region, kind, place) => stable_mir::mir::Rvalue::Ref( + opaque(region), + kind.stable(tables), + place.stable(tables), + ), ThreadLocalRef(def_id) => { stable_mir::mir::Rvalue::ThreadLocalRef(rustc_internal::crate_item(*def_id)) } AddressOf(mutability, place) => { - stable_mir::mir::Rvalue::AddressOf(mutability.stable(), place.stable()) + stable_mir::mir::Rvalue::AddressOf(mutability.stable(tables), place.stable(tables)) } - Len(place) => stable_mir::mir::Rvalue::Len(place.stable()), + Len(place) => stable_mir::mir::Rvalue::Len(place.stable(tables)), Cast(_, _, _) => todo!(), - BinaryOp(bin_op, ops) => { - stable_mir::mir::Rvalue::BinaryOp(bin_op.stable(), ops.0.stable(), ops.1.stable()) - } + BinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::BinaryOp( + bin_op.stable(tables), + ops.0.stable(tables), + ops.1.stable(tables), + ), CheckedBinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::CheckedBinaryOp( - bin_op.stable(), - ops.0.stable(), - ops.1.stable(), + bin_op.stable(tables), + ops.0.stable(tables), + ops.1.stable(tables), ), NullaryOp(_, _) => todo!(), - UnaryOp(un_op, op) => stable_mir::mir::Rvalue::UnaryOp(un_op.stable(), op.stable()), - Discriminant(place) => stable_mir::mir::Rvalue::Discriminant(place.stable()), + UnaryOp(un_op, op) => { + stable_mir::mir::Rvalue::UnaryOp(un_op.stable(tables), op.stable(tables)) + } + Discriminant(place) => stable_mir::mir::Rvalue::Discriminant(place.stable(tables)), Aggregate(_, _) => todo!(), ShallowInitBox(_, _) => todo!(), - CopyForDeref(place) => stable_mir::mir::Rvalue::CopyForDeref(place.stable()), + CopyForDeref(place) => stable_mir::mir::Rvalue::CopyForDeref(place.stable(tables)), } } } -impl Stable for mir::Mutability { +impl<'tcx> Stable<'tcx> for mir::Mutability { type T = stable_mir::mir::Mutability; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { use mir::Mutability::*; match *self { Not => stable_mir::mir::Mutability::Not, @@ -265,21 +277,21 @@ impl Stable for mir::Mutability { } } -impl Stable for mir::BorrowKind { +impl<'tcx> Stable<'tcx> for mir::BorrowKind { type T = stable_mir::mir::BorrowKind; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use mir::BorrowKind::*; match *self { Shared => stable_mir::mir::BorrowKind::Shared, Shallow => stable_mir::mir::BorrowKind::Shallow, - Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable() }, + Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable(tables) }, } } } -impl Stable for mir::MutBorrowKind { +impl<'tcx> Stable<'tcx> for mir::MutBorrowKind { type T = stable_mir::mir::MutBorrowKind; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { use mir::MutBorrowKind::*; match *self { Default => stable_mir::mir::MutBorrowKind::Default, @@ -289,28 +301,28 @@ impl Stable for mir::MutBorrowKind { } } -impl<'tcx> Stable for mir::NullOp<'tcx> { +impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> { type T = stable_mir::mir::NullOp; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use mir::NullOp::*; match self { SizeOf => stable_mir::mir::NullOp::SizeOf, AlignOf => stable_mir::mir::NullOp::AlignOf, - OffsetOf(indices) => { - stable_mir::mir::NullOp::OffsetOf(indices.iter().map(|idx| idx.stable()).collect()) - } + OffsetOf(indices) => stable_mir::mir::NullOp::OffsetOf( + indices.iter().map(|idx| idx.stable(tables)).collect(), + ), } } } -impl Stable for mir::CastKind { +impl<'tcx> Stable<'tcx> for mir::CastKind { type T = stable_mir::mir::CastKind; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use mir::CastKind::*; match self { PointerExposeAddress => stable_mir::mir::CastKind::PointerExposeAddress, PointerFromExposedAddress => stable_mir::mir::CastKind::PointerFromExposedAddress, - PointerCoercion(c) => stable_mir::mir::CastKind::PointerCoercion(c.stable()), + PointerCoercion(c) => stable_mir::mir::CastKind::PointerCoercion(c.stable(tables)), DynStar => stable_mir::mir::CastKind::DynStar, IntToInt => stable_mir::mir::CastKind::IntToInt, FloatToInt => stable_mir::mir::CastKind::FloatToInt, @@ -323,15 +335,15 @@ impl Stable for mir::CastKind { } } -impl Stable for ty::adjustment::PointerCoercion { +impl<'tcx> Stable<'tcx> for ty::adjustment::PointerCoercion { type T = stable_mir::mir::PointerCoercion; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use ty::adjustment::PointerCoercion; match self { PointerCoercion::ReifyFnPointer => stable_mir::mir::PointerCoercion::ReifyFnPointer, PointerCoercion::UnsafeFnPointer => stable_mir::mir::PointerCoercion::UnsafeFnPointer, PointerCoercion::ClosureFnPointer(unsafety) => { - stable_mir::mir::PointerCoercion::ClosureFnPointer(unsafety.stable()) + stable_mir::mir::PointerCoercion::ClosureFnPointer(unsafety.stable(tables)) } PointerCoercion::MutToConstPointer => { stable_mir::mir::PointerCoercion::MutToConstPointer @@ -342,9 +354,9 @@ impl Stable for ty::adjustment::PointerCoercion { } } -impl Stable for rustc_hir::Unsafety { +impl<'tcx> Stable<'tcx> for rustc_hir::Unsafety { type T = stable_mir::mir::Safety; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { match self { rustc_hir::Unsafety::Unsafe => stable_mir::mir::Safety::Unsafe, rustc_hir::Unsafety::Normal => stable_mir::mir::Safety::Normal, @@ -352,28 +364,28 @@ impl Stable for rustc_hir::Unsafety { } } -impl Stable for FieldIdx { +impl<'tcx> Stable<'tcx> for FieldIdx { type T = usize; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { self.as_usize() } } -impl<'tcx> Stable for mir::Operand<'tcx> { +impl<'tcx> Stable<'tcx> for mir::Operand<'tcx> { type T = stable_mir::mir::Operand; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use mir::Operand::*; match self { - Copy(place) => stable_mir::mir::Operand::Copy(place.stable()), - Move(place) => stable_mir::mir::Operand::Move(place.stable()), + Copy(place) => stable_mir::mir::Operand::Copy(place.stable(tables)), + Move(place) => stable_mir::mir::Operand::Move(place.stable(tables)), Constant(c) => stable_mir::mir::Operand::Constant(c.to_string()), } } } -impl<'tcx> Stable for mir::Place<'tcx> { +impl<'tcx> Stable<'tcx> for mir::Place<'tcx> { type T = stable_mir::mir::Place; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { stable_mir::mir::Place { local: self.local.as_usize(), projection: format!("{:?}", self.projection), @@ -381,9 +393,9 @@ impl<'tcx> Stable for mir::Place<'tcx> { } } -impl Stable for mir::UnwindAction { +impl<'tcx> Stable<'tcx> for mir::UnwindAction { type T = stable_mir::mir::UnwindAction; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { use rustc_middle::mir::UnwindAction; match self { UnwindAction::Continue => stable_mir::mir::UnwindAction::Continue, @@ -394,46 +406,48 @@ impl Stable for mir::UnwindAction { } } -impl<'tcx> Stable for mir::AssertMessage<'tcx> { +impl<'tcx> Stable<'tcx> for mir::AssertMessage<'tcx> { type T = stable_mir::mir::AssertMessage; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use rustc_middle::mir::AssertKind; match self { AssertKind::BoundsCheck { len, index } => stable_mir::mir::AssertMessage::BoundsCheck { - len: len.stable(), - index: index.stable(), + len: len.stable(tables), + index: index.stable(tables), }, AssertKind::Overflow(bin_op, op1, op2) => stable_mir::mir::AssertMessage::Overflow( - bin_op.stable(), - op1.stable(), - op2.stable(), + bin_op.stable(tables), + op1.stable(tables), + op2.stable(tables), ), - AssertKind::OverflowNeg(op) => stable_mir::mir::AssertMessage::OverflowNeg(op.stable()), + AssertKind::OverflowNeg(op) => { + stable_mir::mir::AssertMessage::OverflowNeg(op.stable(tables)) + } AssertKind::DivisionByZero(op) => { - stable_mir::mir::AssertMessage::DivisionByZero(op.stable()) + stable_mir::mir::AssertMessage::DivisionByZero(op.stable(tables)) } AssertKind::RemainderByZero(op) => { - stable_mir::mir::AssertMessage::RemainderByZero(op.stable()) + stable_mir::mir::AssertMessage::RemainderByZero(op.stable(tables)) } AssertKind::ResumedAfterReturn(generator) => { - stable_mir::mir::AssertMessage::ResumedAfterReturn(generator.stable()) + stable_mir::mir::AssertMessage::ResumedAfterReturn(generator.stable(tables)) } AssertKind::ResumedAfterPanic(generator) => { - stable_mir::mir::AssertMessage::ResumedAfterPanic(generator.stable()) + stable_mir::mir::AssertMessage::ResumedAfterPanic(generator.stable(tables)) } AssertKind::MisalignedPointerDereference { required, found } => { stable_mir::mir::AssertMessage::MisalignedPointerDereference { - required: required.stable(), - found: found.stable(), + required: required.stable(tables), + found: found.stable(tables), } } } } } -impl Stable for mir::BinOp { +impl<'tcx> Stable<'tcx> for mir::BinOp { type T = stable_mir::mir::BinOp; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { use mir::BinOp; match self { BinOp::Add => stable_mir::mir::BinOp::Add, @@ -462,9 +476,9 @@ impl Stable for mir::BinOp { } } -impl Stable for mir::UnOp { +impl<'tcx> Stable<'tcx> for mir::UnOp { type T = stable_mir::mir::UnOp; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { use mir::UnOp; match self { UnOp::Not => stable_mir::mir::UnOp::Not, @@ -473,9 +487,9 @@ impl Stable for mir::UnOp { } } -impl Stable for rustc_hir::GeneratorKind { +impl<'tcx> Stable<'tcx> for rustc_hir::GeneratorKind { type T = stable_mir::mir::GeneratorKind; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; match self { GeneratorKind::Async(async_gen) => { @@ -491,16 +505,16 @@ impl Stable for rustc_hir::GeneratorKind { } } -impl<'tcx> Stable for mir::InlineAsmOperand<'tcx> { +impl<'tcx> Stable<'tcx> for mir::InlineAsmOperand<'tcx> { type T = stable_mir::mir::InlineAsmOperand; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use rustc_middle::mir::InlineAsmOperand; let (in_value, out_place) = match self { - InlineAsmOperand::In { value, .. } => (Some(value.stable()), None), - InlineAsmOperand::Out { place, .. } => (None, place.map(|place| place.stable())), + InlineAsmOperand::In { value, .. } => (Some(value.stable(tables)), None), + InlineAsmOperand::Out { place, .. } => (None, place.map(|place| place.stable(tables))), InlineAsmOperand::InOut { in_value, out_place, .. } => { - (Some(in_value.stable()), out_place.map(|place| place.stable())) + (Some(in_value.stable(tables)), out_place.map(|place| place.stable(tables))) } InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } @@ -511,15 +525,15 @@ impl<'tcx> Stable for mir::InlineAsmOperand<'tcx> { } } -impl<'tcx> Stable for mir::Terminator<'tcx> { +impl<'tcx> Stable<'tcx> for mir::Terminator<'tcx> { type T = stable_mir::mir::Terminator; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use rustc_middle::mir::TerminatorKind::*; use stable_mir::mir::Terminator; match &self.kind { Goto { target } => Terminator::Goto { target: target.as_usize() }, SwitchInt { discr, targets } => Terminator::SwitchInt { - discr: discr.stable(), + discr: discr.stable(tables), targets: targets .iter() .map(|(value, target)| stable_mir::mir::SwitchTarget { @@ -534,34 +548,34 @@ impl<'tcx> Stable for mir::Terminator<'tcx> { Return => Terminator::Return, Unreachable => Terminator::Unreachable, Drop { place, target, unwind, replace: _ } => Terminator::Drop { - place: place.stable(), + place: place.stable(tables), target: target.as_usize(), - unwind: unwind.stable(), + unwind: unwind.stable(tables), }, Call { func, args, destination, target, unwind, call_source: _, fn_span: _ } => { Terminator::Call { - func: func.stable(), - args: args.iter().map(|arg| arg.stable()).collect(), - destination: destination.stable(), + func: func.stable(tables), + args: args.iter().map(|arg| arg.stable(tables)).collect(), + destination: destination.stable(tables), target: target.map(|t| t.as_usize()), - unwind: unwind.stable(), + unwind: unwind.stable(tables), } } Assert { cond, expected, msg, target, unwind } => Terminator::Assert { - cond: cond.stable(), + cond: cond.stable(tables), expected: *expected, - msg: msg.stable(), + msg: msg.stable(tables), target: target.as_usize(), - unwind: unwind.stable(), + unwind: unwind.stable(tables), }, InlineAsm { template, operands, options, line_spans, destination, unwind } => { Terminator::InlineAsm { template: format!("{:?}", template), - operands: operands.iter().map(|operand| operand.stable()).collect(), + operands: operands.iter().map(|operand| operand.stable(tables)).collect(), options: format!("{:?}", options), line_spans: format!("{:?}", line_spans), destination: destination.map(|d| d.as_usize()), - unwind: unwind.stable(), + unwind: unwind.stable(tables), } } Yield { .. } | GeneratorDrop | FalseEdge { .. } | FalseUnwind { .. } => unreachable!(), From cb8b1d1bc98bf4a8af38bfc751fa150af4571c10 Mon Sep 17 00:00:00 2001 From: Moulins Date: Mon, 19 Jun 2023 15:29:31 +0200 Subject: [PATCH 084/113] add `naive_layout_of` query --- compiler/rustc_middle/src/query/erase.rs | 5 + compiler/rustc_middle/src/query/mod.rs | 11 + compiler/rustc_middle/src/ty/layout.rs | 67 ++++++ compiler/rustc_query_system/src/query/job.rs | 3 +- compiler/rustc_ty_utils/src/layout.rs | 197 ++++++++++++++++-- src/tools/miri/tests/fail/layout_cycle.rs | 2 +- src/tools/miri/tests/fail/layout_cycle.stderr | 7 +- tests/ui/consts/const-size_of-cycle.stderr | 3 +- tests/ui/consts/issue-44415.stderr | 3 +- .../param-env-region-infer.next.stderr | 1 + tests/ui/generics/issue-32498.rs | 1 + ...annot-transmute-unnormalizable-type.stderr | 2 +- .../issue-26548-recursion-via-normalize.rs | 10 +- ...issue-26548-recursion-via-normalize.stderr | 10 +- tests/ui/recursion_limit/zero-overflow.rs | 2 +- tests/ui/recursion_limit/zero-overflow.stderr | 4 +- tests/ui/sized/recursive-type-2.rs | 2 +- tests/ui/sized/recursive-type-2.stderr | 12 +- .../issue-53092-2.stderr | 1 + 19 files changed, 294 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 2c481745d987..a0cb23b5a4c7 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -111,6 +111,11 @@ impl EraseType >()]; } +impl EraseType for Result, &ty::layout::LayoutError<'_>> { + type Result = + [u8; size_of::, &ty::layout::LayoutError<'_>>>()]; +} + impl EraseType for Result, mir::interpret::LitToConstError> { type Result = [u8; size_of::, mir::interpret::LitToConstError>>()]; } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b36f0df78f12..c728cc0b39f8 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1394,6 +1394,17 @@ rustc_queries! { desc { "computing layout of `{}`", key.value } } + /// Computes the naive layout estimate of a type. Note that this implicitly + /// executes in "reveal all" mode, and will normalize the input type. + /// + /// Unlike `layout_of`, this doesn't recurse behind reference types. + query naive_layout_of( + key: ty::ParamEnvAnd<'tcx, Ty<'tcx>> + ) -> Result, &'tcx ty::layout::LayoutError<'tcx>> { + depth_limit + desc { "computing layout (naive) of `{}`", key.value } + } + /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. /// /// NB: this doesn't handle virtual calls - those should use `fn_abi_of_instance` diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 62805d1e8b5c..31005bdd64a0 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -621,6 +621,61 @@ impl MaybeResult for Result { pub type TyAndLayout<'tcx> = rustc_target::abi::TyAndLayout<'tcx, Ty<'tcx>>; +#[derive(Copy, Clone, Debug, HashStable)] +pub struct TyAndNaiveLayout<'tcx> { + pub ty: Ty<'tcx>, + pub layout: NaiveLayout, +} + +impl std::ops::Deref for TyAndNaiveLayout<'_> { + type Target = NaiveLayout; + fn deref(&self) -> &Self::Target { + &self.layout + } +} + +impl std::ops::DerefMut for TyAndNaiveLayout<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.layout + } +} + +/// A naive underestimation of the layout of a type. +#[derive(Copy, Clone, Debug, HashStable)] +pub struct NaiveLayout { + pub min_size: Size, + pub min_align: Align, +} + +impl NaiveLayout { + pub const EMPTY: Self = Self { min_size: Size::ZERO, min_align: Align::ONE }; + + pub fn is_underestimate_of(&self, layout: Layout<'_>) -> bool { + self.min_size <= layout.size() && self.min_align <= layout.align().abi + } + + #[must_use] + pub fn pad_to_align(self) -> Self { + Self { min_size: self.min_size.align_to(self.min_align), min_align: self.min_align } + } + + #[must_use] + pub fn concat(&self, other: &Self, cx: &C) -> Option { + Some(Self { + min_size: self.min_size.checked_add(other.min_size, cx)?, + min_align: std::cmp::max(self.min_align, other.min_align), + }) + } + + #[must_use] + pub fn union(&self, other: &Self) -> Self { + Self { + min_size: std::cmp::max(self.min_size, other.min_size), + min_align: std::cmp::max(self.min_align, other.min_align), + } + } +} + /// Trait for contexts that want to be able to compute layouts of types. /// This automatically gives access to `LayoutOf`, through a blanket `impl`. pub trait LayoutOfHelpers<'tcx>: HasDataLayout + HasTyCtxt<'tcx> + HasParamEnv<'tcx> { @@ -673,6 +728,18 @@ pub trait LayoutOf<'tcx>: LayoutOfHelpers<'tcx> { .map_err(|err| self.handle_layout_err(*err, span, ty)), ) } + + /// Computes the naive layout estimate of a type. Note that this implicitly + /// executes in "reveal all" mode, and will normalize the input type. + /// + /// Unlike `layout_of`, this doesn't recurse behind reference types. + #[inline] + fn naive_layout_of( + &self, + ty: Ty<'tcx>, + ) -> Result, &'tcx LayoutError<'tcx>> { + self.tcx().naive_layout_of(self.param_env().and(ty)) + } } impl<'tcx, C: LayoutOfHelpers<'tcx>> LayoutOf<'tcx> for C {} diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index d2140161f1d9..a53d1fcc69ef 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -176,7 +176,8 @@ impl QueryJobId { while let Some(id) = current_id { let info = query_map.get(&id).unwrap(); // FIXME: This string comparison should probably not be done. - if format!("{:?}", info.query.dep_kind) == "layout_of" { + let query_name = format!("{:?}", info.query.dep_kind); + if query_name == "layout_of" || query_name == "naive_layout_of" { depth += 1; last_layout = Some((info.clone(), depth)); } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index b840ff184e0b..b7e0a3a53a03 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -5,7 +5,8 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::{GeneratorLayout, GeneratorSavedLocal}; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{ - IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES, + IntegerExt, LayoutCx, LayoutError, LayoutOf, NaiveLayout, TyAndLayout, TyAndNaiveLayout, + MAX_SIMD_LANES, }; use rustc_middle::ty::{ self, AdtDef, EarlyBinder, GenericArgsRef, ReprOptions, Ty, TyCtxt, TypeVisitableExt, @@ -24,14 +25,14 @@ use crate::errors::{ use crate::layout_sanity_check::sanity_check_layout; pub fn provide(providers: &mut Providers) { - *providers = Providers { layout_of, ..*providers }; + *providers = Providers { layout_of, naive_layout_of, ..*providers }; } #[instrument(skip(tcx, query), level = "debug")] -fn layout_of<'tcx>( +fn naive_layout_of<'tcx>( tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, -) -> Result, &'tcx LayoutError<'tcx>> { +) -> Result, &'tcx LayoutError<'tcx>> { let (param_env, ty) = query.into_parts(); debug!(?ty); @@ -51,18 +52,45 @@ fn layout_of<'tcx>( } }; + if ty != unnormalized_ty { + // Ensure this layout is also cached for the normalized type. + return tcx.naive_layout_of(param_env.and(ty)); + } + + let cx = LayoutCx { tcx, param_env }; + let layout = naive_layout_of_uncached(&cx, ty)?; + Ok(TyAndNaiveLayout { ty, layout }) +} + +#[instrument(skip(tcx, query), level = "debug")] +fn layout_of<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, +) -> Result, &'tcx LayoutError<'tcx>> { + let (param_env, unnormalized_ty) = query.into_parts(); + let param_env = param_env.with_reveal_all_normalized(tcx); + // `naive_layout_of` takes care of normalizing the type. + let naive = tcx.naive_layout_of(query)?; + let ty = naive.ty; + if ty != unnormalized_ty { // Ensure this layout is also cached for the normalized type. return tcx.layout_of(param_env.and(ty)); } let cx = LayoutCx { tcx, param_env }; - let layout = layout_of_uncached(&cx, ty)?; + + if !naive.is_underestimate_of(layout) { + bug!( + "the estimated naive layout is bigger than the actual layout:\n{:#?}\n{:#?}", + naive, + layout, + ); + } + let layout = TyAndLayout { ty, layout }; - record_layout_for_printing(&cx, layout); - sanity_check_layout(&cx, &layout); Ok(layout) @@ -75,6 +103,132 @@ fn error<'tcx>( cx.tcx.arena.alloc(err) } +fn naive_layout_of_uncached<'tcx>( + cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, + ty: Ty<'tcx>, +) -> Result> { + let tcx = cx.tcx; + let dl = cx.data_layout(); + + let scalar = + |value: Primitive| NaiveLayout { min_size: value.size(dl), min_align: value.align(dl).abi }; + + let univariant = |fields: &mut dyn Iterator>, + repr: &ReprOptions| + -> Result> { + // For simplicity, ignore inter-field padding; this may underestimate the size. + // FIXME(reference_niches): Be smarter and implement something closer to the real layout logic. + let mut layout = NaiveLayout::EMPTY; + for field in fields { + let field = cx.naive_layout_of(field)?; + layout = layout + .concat(&field, cx) + .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?; + } + + if let Some(align) = repr.align { + layout.min_align = std::cmp::max(layout.min_align, align); + } + if let Some(pack) = repr.pack { + layout.min_align = std::cmp::min(layout.min_align, pack); + } + + Ok(layout.pad_to_align()) + }; + + debug_assert!(!ty.has_non_region_infer()); + + Ok(match *ty.kind() { + // Basic scalars + ty::Bool => scalar(Int(I8, false)), + ty::Char => scalar(Int(I32, false)), + ty::Int(ity) => scalar(Int(Integer::from_int_ty(dl, ity), true)), + ty::Uint(ity) => scalar(Int(Integer::from_uint_ty(dl, ity), false)), + ty::Float(fty) => scalar(match fty { + ty::FloatTy::F32 => F32, + ty::FloatTy::F64 => F64, + }), + ty::FnPtr(_) => scalar(Pointer(dl.instruction_address_space)), + + // The never type. + ty::Never => NaiveLayout::EMPTY, + + // Potentially-wide pointers. + ty::Ref(_, _, _) | ty::RawPtr(_) => { + // TODO(reference_niches): handle wide pointers + scalar(Pointer(AddressSpace::DATA)) + } + + ty::Dynamic(_, _, ty::DynStar) => { + let ptr = scalar(Pointer(AddressSpace::DATA)); + ptr.concat(&ptr, cx).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))? + } + + // Arrays and slices. + ty::Array(element, _count) => { + let element = cx.naive_layout_of(element)?; + NaiveLayout { + min_size: Size::ZERO, // TODO(reference_niches): proper array size + min_align: element.min_align, + } + } + ty::Slice(element) => { + NaiveLayout { min_size: Size::ZERO, min_align: cx.naive_layout_of(element)?.min_align } + } + ty::Str => NaiveLayout::EMPTY, + + // Odd unit types. + ty::FnDef(..) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => NaiveLayout::EMPTY, + + // FIXME(reference_niches): try to actually compute a reasonable layout estimate, + // without duplicating too much code from `generator_layout`. + ty::Generator(..) => NaiveLayout::EMPTY, + + ty::Closure(_, ref substs) => { + univariant(&mut substs.as_closure().upvar_tys(), &ReprOptions::default())? + } + + ty::Tuple(tys) => univariant(&mut tys.iter(), &ReprOptions::default())?, + + ty::Adt(def, substs) if def.is_union() => { + let repr = def.repr(); + let only_variant = &def.variants()[FIRST_VARIANT]; + only_variant.fields.iter().try_fold(NaiveLayout::EMPTY, |layout, f| { + let mut fields = std::iter::once(f.ty(tcx, substs)); + univariant(&mut fields, &repr).map(|l| layout.union(&l)) + })? + } + + ty::Adt(def, substs) => { + // For simplicity, assume that any discriminant field (if it exists) + // gets niched inside one of the variants; this will underestimate the size + // (and sometimes alignment) of enums. + // FIXME(reference_niches): Be smarter and actually take into accoount the discriminant. + let repr = def.repr(); + def.variants().iter().try_fold(NaiveLayout::EMPTY, |layout, v| { + let mut fields = v.fields.iter().map(|f| f.ty(tcx, substs)); + let vlayout = univariant(&mut fields, &repr)?; + Ok(layout.union(&vlayout)) + })? + } + + // Types with no meaningful known layout. + ty::Alias(..) => { + // NOTE(eddyb) `layout_of` query should've normalized these away, + // if that was possible, so there's no reason to try again here. + return Err(error(cx, LayoutError::Unknown(ty))); + } + + ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => { + bug!("Layout::compute: unexpected type `{}`", ty) + } + + ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => { + return Err(error(cx, LayoutError::Unknown(ty))); + } + }) +} + fn univariant_uninterned<'tcx>( cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, ty: Ty<'tcx>, @@ -146,6 +300,14 @@ fn layout_of_uncached<'tcx>( ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { let mut data_ptr = scalar_unit(Pointer(AddressSpace::DATA)); if !ty.is_unsafe_ptr() { + match cx.naive_layout_of(pointee) { + // TODO(reference_niches): actually use the naive layout to set + // reference niches; the query is still kept to for testing purposes. + Ok(_) => (), + // This can happen when computing the `SizeSkeleton` of a generic type. + Err(LayoutError::Unknown(_)) => (), + Err(err) => return Err(err), + } data_ptr.valid_range_mut().start = 1; } @@ -558,18 +720,15 @@ fn layout_of_uncached<'tcx>( } // Types with no meaningful known layout. - ty::Alias(..) => { - // NOTE(eddyb) `layout_of` query should've normalized these away, - // if that was possible, so there's no reason to try again here. - return Err(error(cx, LayoutError::Unknown(ty))); - } - - ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => { - bug!("Layout::compute: unexpected type `{}`", ty) - } - - ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => { - return Err(error(cx, LayoutError::Unknown(ty))); + ty::Alias(..) + | ty::Bound(..) + | ty::GeneratorWitness(..) + | ty::GeneratorWitnessMIR(..) + | ty::Infer(_) + | ty::Placeholder(..) + | ty::Param(_) + | ty::Error(_) => { + unreachable!("already rejected by `naive_layout_of`"); } }) } diff --git a/src/tools/miri/tests/fail/layout_cycle.rs b/src/tools/miri/tests/fail/layout_cycle.rs index 3e0dd881db84..d6a937de15ca 100644 --- a/src/tools/miri/tests/fail/layout_cycle.rs +++ b/src/tools/miri/tests/fail/layout_cycle.rs @@ -1,5 +1,5 @@ //@error-in-other-file: a cycle occurred during layout computation -//~^ ERROR: cycle detected when computing layout of +//~^ ERROR: cycle detected when computing layout (naive) of use std::mem; diff --git a/src/tools/miri/tests/fail/layout_cycle.stderr b/src/tools/miri/tests/fail/layout_cycle.stderr index 38907a1c50cc..ccf93a9def49 100644 --- a/src/tools/miri/tests/fail/layout_cycle.stderr +++ b/src/tools/miri/tests/fail/layout_cycle.stderr @@ -1,7 +1,8 @@ -error[E0391]: cycle detected when computing layout of `S>` +error[E0391]: cycle detected when computing layout (naive) of `S>` | - = note: ...which requires computing layout of ` as Tr>::I`... - = note: ...which again requires computing layout of `S>`, completing the cycle + = note: ...which requires computing layout (naive) of ` as Tr>::I`... + = note: ...which again requires computing layout (naive) of `S>`, completing the cycle + = note: cycle used when computing layout of `S>` = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: post-monomorphization error: a cycle occurred during layout computation diff --git a/tests/ui/consts/const-size_of-cycle.stderr b/tests/ui/consts/const-size_of-cycle.stderr index 46b432357aae..08f0c1563ccb 100644 --- a/tests/ui/consts/const-size_of-cycle.stderr +++ b/tests/ui/consts/const-size_of-cycle.stderr @@ -15,7 +15,8 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`.. LL | bytes: [u8; std::mem::size_of::()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which requires computing layout of `Foo`... - = note: ...which requires computing layout of `[u8; std::mem::size_of::()]`... + = note: ...which requires computing layout (naive) of `Foo`... + = note: ...which requires computing layout (naive) of `[u8; std::mem::size_of::()]`... = note: ...which requires normalizing `[u8; std::mem::size_of::()]`... = note: ...which again requires evaluating type-level constant, completing the cycle note: cycle used when checking that `Foo` is well-formed diff --git a/tests/ui/consts/issue-44415.stderr b/tests/ui/consts/issue-44415.stderr index 01d24a620814..7ff413def86d 100644 --- a/tests/ui/consts/issue-44415.stderr +++ b/tests/ui/consts/issue-44415.stderr @@ -15,7 +15,8 @@ note: ...which requires const-evaluating + checking `Foo::bytes::{constant#0}`.. LL | bytes: [u8; unsafe { intrinsics::size_of::() }], | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which requires computing layout of `Foo`... - = note: ...which requires computing layout of `[u8; unsafe { intrinsics::size_of::() }]`... + = note: ...which requires computing layout (naive) of `Foo`... + = note: ...which requires computing layout (naive) of `[u8; unsafe { intrinsics::size_of::() }]`... = note: ...which requires normalizing `[u8; unsafe { intrinsics::size_of::() }]`... = note: ...which again requires evaluating type-level constant, completing the cycle note: cycle used when checking that `Foo` is well-formed diff --git a/tests/ui/dyn-star/param-env-region-infer.next.stderr b/tests/ui/dyn-star/param-env-region-infer.next.stderr index 28aec533a006..d86405462f42 100644 --- a/tests/ui/dyn-star/param-env-region-infer.next.stderr +++ b/tests/ui/dyn-star/param-env-region-infer.next.stderr @@ -10,6 +10,7 @@ note: ...which requires type-checking `make_dyn_star`... LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which requires computing layout of `make_dyn_star::{opaque#0}`... + = note: ...which requires computing layout (naive) of `make_dyn_star::{opaque#0}`... = note: ...which requires normalizing `make_dyn_star::{opaque#0}`... = note: ...which again requires computing type of `make_dyn_star::{opaque#0}`, completing the cycle note: cycle used when checking item types in top-level module diff --git a/tests/ui/generics/issue-32498.rs b/tests/ui/generics/issue-32498.rs index 1b54401097ea..0abd5b1a9b14 100644 --- a/tests/ui/generics/issue-32498.rs +++ b/tests/ui/generics/issue-32498.rs @@ -1,5 +1,6 @@ // run-pass #![allow(dead_code)] +#![recursion_limit = "129"] // Making sure that no overflow occurs. diff --git a/tests/ui/layout/cannot-transmute-unnormalizable-type.stderr b/tests/ui/layout/cannot-transmute-unnormalizable-type.stderr index dd5119318ff4..b9d82bb0f93f 100644 --- a/tests/ui/layout/cannot-transmute-unnormalizable-type.stderr +++ b/tests/ui/layout/cannot-transmute-unnormalizable-type.stderr @@ -11,7 +11,7 @@ LL | std::mem::transmute::, Option<&Other>>(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: source type: `Option<()>` (8 bits) - = note: target type: `Option<&Other>` (unable to determine layout for `Other` because `<() as Trait>::RefTarget` cannot be normalized) + = note: target type: `Option<&Other>` (unable to determine layout for `<() as Trait>::RefTarget` because `<() as Trait>::RefTarget` cannot be normalized) error: aborting due to 2 previous errors diff --git a/tests/ui/recursion/issue-26548-recursion-via-normalize.rs b/tests/ui/recursion/issue-26548-recursion-via-normalize.rs index 6c7fc4beb543..14bc74f57f61 100644 --- a/tests/ui/recursion/issue-26548-recursion-via-normalize.rs +++ b/tests/ui/recursion/issue-26548-recursion-via-normalize.rs @@ -1,9 +1,9 @@ -//~ ERROR cycle detected when computing layout of `core::option::Option` +//~ ERROR cycle detected when computing layout (naive) of `core::option::Option` //~| NOTE see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -//~| NOTE ...which requires computing layout of `S`... -//~| NOTE ...which requires computing layout of `core::option::Option<::It>`... -//~| NOTE ...which again requires computing layout of `core::option::Option`, completing the cycle -//~| NOTE cycle used when computing layout of `core::option::Option<::It>` +//~| NOTE ...which requires computing layout (naive) of `S`... +//~| NOTE ...which requires computing layout (naive) of `core::option::Option<::It>`... +//~| NOTE ...which again requires computing layout (naive) of `core::option::Option`, completing the cycle +//~| NOTE cycle used when computing layout (naive) of `core::option::Option<::It>` trait Mirror { type It: ?Sized; diff --git a/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr b/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr index 514bed607003..109ba278232a 100644 --- a/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr +++ b/tests/ui/recursion/issue-26548-recursion-via-normalize.stderr @@ -1,9 +1,9 @@ -error[E0391]: cycle detected when computing layout of `core::option::Option` +error[E0391]: cycle detected when computing layout (naive) of `core::option::Option` | - = note: ...which requires computing layout of `S`... - = note: ...which requires computing layout of `core::option::Option<::It>`... - = note: ...which again requires computing layout of `core::option::Option`, completing the cycle - = note: cycle used when computing layout of `core::option::Option<::It>` + = note: ...which requires computing layout (naive) of `S`... + = note: ...which requires computing layout (naive) of `core::option::Option<::It>`... + = note: ...which again requires computing layout (naive) of `core::option::Option`, completing the cycle + = note: cycle used when computing layout (naive) of `core::option::Option<::It>` = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to previous error diff --git a/tests/ui/recursion_limit/zero-overflow.rs b/tests/ui/recursion_limit/zero-overflow.rs index 77bd81856760..98b3da651352 100644 --- a/tests/ui/recursion_limit/zero-overflow.rs +++ b/tests/ui/recursion_limit/zero-overflow.rs @@ -1,4 +1,4 @@ -//~ ERROR overflow evaluating the requirement `&mut Self: DispatchFromDyn<&mut RustaceansAreAwesome> +//~ ERROR queries overflow the depth limit! //~| HELP consider increasing the recursion limit // build-fail diff --git a/tests/ui/recursion_limit/zero-overflow.stderr b/tests/ui/recursion_limit/zero-overflow.stderr index 9007ec0d7844..172c767d9f0c 100644 --- a/tests/ui/recursion_limit/zero-overflow.stderr +++ b/tests/ui/recursion_limit/zero-overflow.stderr @@ -1,7 +1,7 @@ -error[E0275]: overflow evaluating the requirement `&mut Self: DispatchFromDyn<&mut RustaceansAreAwesome>` +error: queries overflow the depth limit! | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`zero_overflow`) + = note: query depth increased by 2 when computing layout of `()` error: aborting due to previous error -For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/sized/recursive-type-2.rs b/tests/ui/sized/recursive-type-2.rs index 7d95417a6ffd..7ee5ee854d4b 100644 --- a/tests/ui/sized/recursive-type-2.rs +++ b/tests/ui/sized/recursive-type-2.rs @@ -1,5 +1,5 @@ // build-fail -//~^ ERROR cycle detected when computing layout of `Foo<()>` +//~^ ERROR cycle detected when computing layout (naive) of `Foo<()>` trait A { type Assoc: ?Sized; } diff --git a/tests/ui/sized/recursive-type-2.stderr b/tests/ui/sized/recursive-type-2.stderr index 0f72f74145e8..502b0a4352c7 100644 --- a/tests/ui/sized/recursive-type-2.stderr +++ b/tests/ui/sized/recursive-type-2.stderr @@ -1,12 +1,8 @@ -error[E0391]: cycle detected when computing layout of `Foo<()>` +error[E0391]: cycle detected when computing layout (naive) of `Foo<()>` | - = note: ...which requires computing layout of `<() as A>::Assoc`... - = note: ...which again requires computing layout of `Foo<()>`, completing the cycle -note: cycle used when elaborating drops for `main` - --> $DIR/recursive-type-2.rs:11:1 - | -LL | fn main() { - | ^^^^^^^^^ + = note: ...which requires computing layout (naive) of `<() as A>::Assoc`... + = note: ...which again requires computing layout (naive) of `Foo<()>`, completing the cycle + = note: cycle used when computing layout of `Foo<()>` = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to previous error diff --git a/tests/ui/type-alias-impl-trait/issue-53092-2.stderr b/tests/ui/type-alias-impl-trait/issue-53092-2.stderr index 6148131b491f..dd6793242807 100644 --- a/tests/ui/type-alias-impl-trait/issue-53092-2.stderr +++ b/tests/ui/type-alias-impl-trait/issue-53092-2.stderr @@ -10,6 +10,7 @@ note: ...which requires type-checking `CONST_BUG`... LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which requires computing layout of `Bug`... + = note: ...which requires computing layout (naive) of `Bug`... = note: ...which requires normalizing `Bug`... = note: ...which again requires computing type of `Bug::{opaque#0}`, completing the cycle note: cycle used when checking item types in top-level module From 30ae640a3c4ebe70ec5bb1b8782f9e8206ed324e Mon Sep 17 00:00:00 2001 From: Moulins Date: Mon, 19 Jun 2023 19:26:53 +0200 Subject: [PATCH 085/113] properly handle arrays and wide pointers in `naive_layout_of` --- compiler/rustc_ty_utils/src/layout.rs | 195 +++++++++++------- ...annot-transmute-unnormalizable-type.stderr | 2 +- 2 files changed, 116 insertions(+), 81 deletions(-) diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index b7e0a3a53a03..2afe1cb37bb3 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -154,9 +154,19 @@ fn naive_layout_of_uncached<'tcx>( ty::Never => NaiveLayout::EMPTY, // Potentially-wide pointers. - ty::Ref(_, _, _) | ty::RawPtr(_) => { - // TODO(reference_niches): handle wide pointers - scalar(Pointer(AddressSpace::DATA)) + ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + let data_ptr = scalar(Pointer(AddressSpace::DATA)); + + if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? { + // Effectively a (ptr, meta) tuple. + data_ptr + .concat(&scalar(metadata.primitive()), cx) + .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))? + .pad_to_align() + } else { + // No metadata, this is a thin pointer. + data_ptr + } } ty::Dynamic(_, _, ty::DynStar) => { @@ -165,10 +175,15 @@ fn naive_layout_of_uncached<'tcx>( } // Arrays and slices. - ty::Array(element, _count) => { + ty::Array(element, count) => { + let count = compute_array_count(cx, count) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; let element = cx.naive_layout_of(element)?; NaiveLayout { - min_size: Size::ZERO, // TODO(reference_niches): proper array size + min_size: element + .min_size + .checked_mul(count, cx) + .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?, min_align: element.min_align, } } @@ -311,72 +326,13 @@ fn layout_of_uncached<'tcx>( data_ptr.valid_range_mut().start = 1; } - let pointee = tcx.normalize_erasing_regions(param_env, pointee); - if pointee.is_sized(tcx, param_env) { - return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr))); - } - - let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() - // Projection eagerly bails out when the pointee references errors, - // fall back to structurally deducing metadata. - && !pointee.references_error() - { - let pointee_metadata = Ty::new_projection(tcx,metadata_def_id, [pointee]); - let metadata_ty = match tcx.try_normalize_erasing_regions( - param_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. - match tcx.try_normalize_erasing_regions( - param_env, - tcx.struct_tail_without_normalization(pointee), - ) { - Ok(_) => {}, - Err(better_err) => { - err = better_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. - if metadata_layout.is_zst() && metadata_layout.align.abi.bytes() == 1 { - return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr))); - } - - let Abi::Scalar(metadata) = metadata_layout.abi else { - return Err(error(cx, LayoutError::Unknown(pointee))); - }; - - metadata + if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? { + // Effectively a (ptr, meta) tuple. + tcx.mk_layout(cx.scalar_pair(data_ptr, metadata)) } else { - let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env); - - match unsized_part.kind() { - ty::Foreign(..) => { - return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr))); - } - ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)), - ty::Dynamic(..) => { - let mut vtable = scalar_unit(Pointer(AddressSpace::DATA)); - vtable.valid_range_mut().start = 1; - vtable - } - _ => { - return Err(error(cx, LayoutError::Unknown(pointee))); - } - } - }; - - // Effectively a (ptr, meta) tuple. - tcx.mk_layout(cx.scalar_pair(data_ptr, metadata)) + // No metadata, this is a thin pointer. + tcx.mk_layout(LayoutS::scalar(cx, data_ptr)) + } } ty::Dynamic(_, _, ty::DynStar) => { @@ -388,16 +344,8 @@ fn layout_of_uncached<'tcx>( } // Arrays and slices. - ty::Array(element, mut count) => { - if count.has_projections() { - count = tcx.normalize_erasing_regions(param_env, count); - if count.has_projections() { - return Err(error(cx, LayoutError::Unknown(ty))); - } - } - - let count = count - .try_eval_target_usize(tcx, param_env) + ty::Array(element, count) => { + let count = compute_array_count(cx, count) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; let element = cx.layout_of(element)?; let size = element @@ -733,6 +681,93 @@ fn layout_of_uncached<'tcx>( }) } +fn compute_array_count<'tcx>( + cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, + mut count: ty::Const<'tcx>, +) -> Option { + let LayoutCx { tcx, param_env } = *cx; + if count.has_projections() { + count = tcx.normalize_erasing_regions(param_env, count); + if count.has_projections() { + return None; + } + } + + count.try_eval_target_usize(tcx, param_env) +} + +fn ptr_metadata_scalar<'tcx>( + cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, + pointee: Ty<'tcx>, +) -> Result, &'tcx LayoutError<'tcx>> { + let dl = cx.data_layout(); + let scalar_unit = |value: Primitive| { + let size = value.size(dl); + assert!(size.bits() <= 128); + Scalar::Initialized { value, valid_range: WrappingRange::full(size) } + }; + + let LayoutCx { tcx, param_env } = *cx; + + let pointee = tcx.normalize_erasing_regions(param_env, pointee); + if pointee.is_sized(tcx, param_env) { + return Ok(None); + } + + if let Some(metadata_def_id) = tcx.lang_items().metadata_type() + // Projection eagerly bails out when the pointee references errors, + // fall back to structurally deducing metadata. + && !pointee.references_error() + { + let pointee_metadata = Ty::new_projection(tcx,metadata_def_id, [pointee]); + let metadata_ty = match tcx.try_normalize_erasing_regions( + param_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. + match tcx.try_normalize_erasing_regions( + param_env, + tcx.struct_tail_without_normalization(pointee), + ) { + Ok(_) => {}, + Err(better_err) => { + err = better_err; + } + } + return Err(error(cx, LayoutError::NormalizationFailure(pointee, err))); + }, + }; + + let metadata_layout = cx.layout_of(metadata_ty)?; + + if metadata_layout.is_zst() && metadata_layout.align.abi.bytes() == 1 { + Ok(None) // If the metadata is a 1-zst, then the pointer is thin. + } else if let Abi::Scalar(metadata) = metadata_layout.abi { + Ok(Some(metadata)) + } else { + Err(error(cx, LayoutError::Unknown(pointee))) + } + } else { + let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env); + + match unsized_part.kind() { + ty::Foreign(..) => Ok(None), + ty::Slice(_) | ty::Str => Ok(Some(scalar_unit(Int(dl.ptr_sized_integer(), false)))), + ty::Dynamic(..) => { + let mut vtable = scalar_unit(Pointer(AddressSpace::DATA)); + vtable.valid_range_mut().start = 1; + Ok(Some(vtable)) + } + _ => Err(error(cx, LayoutError::Unknown(pointee))), + } + } +} + /// Overlap eligibility and variant assignment for each GeneratorSavedLocal. #[derive(Clone, Debug, PartialEq)] enum SavedLocalEligibility { diff --git a/tests/ui/layout/cannot-transmute-unnormalizable-type.stderr b/tests/ui/layout/cannot-transmute-unnormalizable-type.stderr index b9d82bb0f93f..dd5119318ff4 100644 --- a/tests/ui/layout/cannot-transmute-unnormalizable-type.stderr +++ b/tests/ui/layout/cannot-transmute-unnormalizable-type.stderr @@ -11,7 +11,7 @@ LL | std::mem::transmute::, Option<&Other>>(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: source type: `Option<()>` (8 bits) - = note: target type: `Option<&Other>` (unable to determine layout for `<() as Trait>::RefTarget` because `<() as Trait>::RefTarget` cannot be normalized) + = note: target type: `Option<&Other>` (unable to determine layout for `Other` because `<() as Trait>::RefTarget` cannot be normalized) error: aborting due to 2 previous errors From 8b847ef734e2bf2d424cf111f671978505c04cf1 Mon Sep 17 00:00:00 2001 From: Moulins Date: Fri, 23 Jun 2023 21:05:24 +0200 Subject: [PATCH 086/113] add crate-local `-Z reference_niches` unstable flag (does nothing for now) --- compiler/rustc_abi/src/lib.rs | 8 +++++ compiler/rustc_interface/src/tests.rs | 2 ++ .../src/rmeta/decoder/cstore_impl.rs | 1 + compiler/rustc_metadata/src/rmeta/encoder.rs | 1 + compiler/rustc_metadata/src/rmeta/mod.rs | 3 +- compiler/rustc_middle/src/query/erase.rs | 1 + compiler/rustc_middle/src/query/mod.rs | 5 ++++ compiler/rustc_session/src/config.rs | 2 ++ compiler/rustc_session/src/options.rs | 30 +++++++++++++++++++ compiler/rustc_ty_utils/src/layout.rs | 11 +++++-- 10 files changed, 61 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index ef0c763ac203..29b3c44dcd63 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -49,6 +49,14 @@ bitflags! { } } +/// Which niches (beyond the `null` niche) are available on references. +#[derive(Default, Copy, Clone, Hash, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))] +pub struct ReferenceNichePolicy { + pub size: bool, + pub align: bool, +} + #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))] pub enum IntegerType { diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 5c6c3491b388..49bd7b6aa9c5 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -28,6 +28,7 @@ use rustc_span::edition::{Edition, DEFAULT_EDITION}; use rustc_span::symbol::sym; use rustc_span::FileName; use rustc_span::SourceFileHashAlgorithm; +use rustc_target::abi::ReferenceNichePolicy; use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel}; use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel}; @@ -819,6 +820,7 @@ fn test_unstable_options_tracking_hash() { tracked!(profile_emit, Some(PathBuf::from("abc"))); tracked!(profile_sample_use, Some(PathBuf::from("abc"))); tracked!(profiler_runtime, "abc".to_string()); + tracked!(reference_niches, Some(ReferenceNichePolicy { size: true, align: false })); tracked!(relax_elf_relocations, Some(true)); tracked!(relro_level, Some(RelroLevel::Full)); tracked!(remap_cwd_prefix, Some(PathBuf::from("abc"))); diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index a8815ee0908d..77c33336dff6 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -301,6 +301,7 @@ provide! { tcx, def_id, other, cdata, is_profiler_runtime => { cdata.root.profiler_runtime } required_panic_strategy => { cdata.root.required_panic_strategy } panic_in_drop_strategy => { cdata.root.panic_in_drop_strategy } + reference_niches_policy => { cdata.root.reference_niches_policy } extern_crate => { let r = *cdata.extern_crate.lock(); r.map(|c| &*tcx.arena.alloc(c)) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index ac86110f2bdb..46571e7796d0 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -673,6 +673,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(), required_panic_strategy: tcx.required_panic_strategy(LOCAL_CRATE), panic_in_drop_strategy: tcx.sess.opts.unstable_opts.panic_in_drop, + reference_niches_policy: tcx.reference_niches_policy(LOCAL_CRATE), edition: tcx.sess.edition(), has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE), has_alloc_error_handler: tcx.has_alloc_error_handler(LOCAL_CRATE), diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 0bc16fc64ff7..8bc2e0aa5a9e 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -32,7 +32,7 @@ use rustc_span::edition::Edition; use rustc_span::hygiene::{ExpnIndex, MacroKind}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Span}; -use rustc_target::abi::{FieldIdx, VariantIdx}; +use rustc_target::abi::{FieldIdx, ReferenceNichePolicy, VariantIdx}; use rustc_target::spec::{PanicStrategy, TargetTriple}; use std::marker::PhantomData; @@ -251,6 +251,7 @@ pub(crate) struct CrateRoot { stable_crate_id: StableCrateId, required_panic_strategy: Option, panic_in_drop_strategy: PanicStrategy, + reference_niches_policy: ReferenceNichePolicy, edition: Edition, has_global_allocator: bool, has_alloc_error_handler: bool, diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index a0cb23b5a4c7..9bf022670053 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -296,6 +296,7 @@ trivial! { rustc_span::Symbol, rustc_span::symbol::Ident, rustc_target::spec::PanicStrategy, + rustc_target::abi::ReferenceNichePolicy, rustc_type_ir::Variance, u32, usize, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index c728cc0b39f8..51409f908431 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1480,6 +1480,11 @@ rustc_queries! { desc { "getting a crate's configured panic-in-drop strategy" } separate_provide_extern } + query reference_niches_policy(_: CrateNum) -> abi::ReferenceNichePolicy { + fatal_cycle + desc { "getting a crate's policy for size and alignment niches of references" } + separate_provide_extern + } query is_no_builtins(_: CrateNum) -> bool { fatal_cycle desc { "getting whether a crate has `#![no_builtins]`" } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 593983f117d0..43dae9e8de2d 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3076,6 +3076,7 @@ pub(crate) mod dep_tracking { use rustc_feature::UnstableFeatures; use rustc_span::edition::Edition; use rustc_span::RealFileName; + use rustc_target::abi::ReferenceNichePolicy; use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel}; use rustc_target::spec::{ RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, @@ -3171,6 +3172,7 @@ pub(crate) mod dep_tracking { OomStrategy, LanguageIdentifier, TraitSolver, + ReferenceNichePolicy, ); impl DepTrackingHash for (T1, T2) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 87d67c099ced..0d63db2bfef1 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -6,6 +6,7 @@ use crate::{lint, EarlyErrorHandler}; use rustc_data_structures::profiling::TimePassesFormat; use rustc_errors::ColorConfig; use rustc_errors::{LanguageIdentifier, TerminalUrl}; +use rustc_target::abi::ReferenceNichePolicy; use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet}; use rustc_target::spec::{ RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, @@ -421,6 +422,8 @@ mod desc { pub const parse_proc_macro_execution_strategy: &str = "one of supported execution strategies (`same-thread`, or `cross-thread`)"; pub const parse_dump_solver_proof_tree: &str = "one of: `always`, `on-request`, `on-error`"; + pub const parse_opt_reference_niches: &str = + "`null`, or a `,` separated combination of `size` or `align`"; } mod parse { @@ -1253,6 +1256,31 @@ mod parse { }; true } + + pub(crate) fn parse_opt_reference_niches( + slot: &mut Option, + v: Option<&str>, + ) -> bool { + let Some(s) = v else { + return false; + }; + + let slot = slot.get_or_insert_default(); + + if s == "null" { + return true; + } + + for opt in s.split(",") { + match opt { + "size" => slot.size = true, + "align" => slot.align = true, + _ => return false, + } + } + + true + } } options! { @@ -1698,6 +1726,8 @@ options! { "enable queries of the dependency graph for regression testing (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], "randomize the layout of types (default: no)"), + reference_niches: Option = (None, parse_opt_reference_niches, [TRACKED], + "override the set of discriminant niches that may be exposed by references"), relax_elf_relocations: Option = (None, parse_opt_bool, [TRACKED], "whether ELF relocations can be relaxed"), relro_level: Option = (None, parse_relro_level, [TRACKED], diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 2afe1cb37bb3..19545a1b135b 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -3,7 +3,7 @@ use rustc_hir as hir; use rustc_index::bit_set::BitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::{GeneratorLayout, GeneratorSavedLocal}; -use rustc_middle::query::Providers; +use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::layout::{ IntegerExt, LayoutCx, LayoutError, LayoutOf, NaiveLayout, TyAndLayout, TyAndNaiveLayout, MAX_SIMD_LANES, @@ -25,7 +25,14 @@ use crate::errors::{ use crate::layout_sanity_check::sanity_check_layout; pub fn provide(providers: &mut Providers) { - *providers = Providers { layout_of, naive_layout_of, ..*providers }; + *providers = Providers { layout_of, naive_layout_of, reference_niches_policy, ..*providers }; +} + +#[instrument(skip(tcx), level = "debug")] +fn reference_niches_policy<'tcx>(tcx: TyCtxt<'tcx>, _: LocalCrate) -> ReferenceNichePolicy { + const DEFAULT: ReferenceNichePolicy = ReferenceNichePolicy { size: false, align: false }; + + tcx.sess.opts.unstable_opts.reference_niches.unwrap_or(DEFAULT) } #[instrument(skip(tcx, query), level = "debug")] From 3c0527686644cb30291a03bdecdcfddb396765ab Mon Sep 17 00:00:00 2001 From: Moulins Date: Sat, 24 Jun 2023 01:34:14 +0200 Subject: [PATCH 087/113] restrict the valid range of references if `-Z reference-niches` is set Note that this doesn't actually work at all, as many places in rustc assume that references only have the null niche. --- compiler/rustc_abi/src/lib.rs | 31 +++++++++++++++++++ compiler/rustc_ty_utils/src/layout.rs | 44 ++++++++++++++++++++------- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 29b3c44dcd63..0839e95723a8 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -354,6 +354,31 @@ impl TargetDataLayout { } } + /// Returns the theoretical maximum address. + /// + /// Note that this doesn't take into account target-specific limitations. + #[inline] + pub fn max_address(&self) -> u64 { + match self.pointer_size.bits() { + 16 => u16::MAX.into(), + 32 => u32::MAX.into(), + 64 => u64::MAX, + bits => panic!("max_address: unknown pointer bit size {}", bits), + } + } + + /// Returns the (inclusive) range of possible addresses for an allocation with + /// the given size and alignment. + /// + /// Note that this doesn't take into account target-specific limitations. + #[inline] + pub fn address_range_for(&self, size: Size, align: Align) -> (u64, u64) { + let end = Size::from_bytes(self.max_address()); + let min = align.bytes(); + let max = (end - size).align_down_to(align).bytes(); + (min, max) + } + #[inline] pub fn vector_align(&self, vec_size: Size) -> AbiAndPrefAlign { for &(size, align) in &self.vector_align { @@ -481,6 +506,12 @@ impl Size { Size::from_bytes((self.bytes() + mask) & !mask) } + #[inline] + pub fn align_down_to(self, align: Align) -> Size { + let mask = align.bytes() - 1; + Size::from_bytes(self.bytes() & !mask) + } + #[inline] pub fn is_aligned(self, align: Align) -> bool { let mask = align.bytes() - 1; diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 19545a1b135b..c81e76e44716 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -30,11 +30,13 @@ pub fn provide(providers: &mut Providers) { #[instrument(skip(tcx), level = "debug")] fn reference_niches_policy<'tcx>(tcx: TyCtxt<'tcx>, _: LocalCrate) -> ReferenceNichePolicy { - const DEFAULT: ReferenceNichePolicy = ReferenceNichePolicy { size: false, align: false }; - - tcx.sess.opts.unstable_opts.reference_niches.unwrap_or(DEFAULT) + tcx.sess.opts.unstable_opts.reference_niches.unwrap_or(DEFAULT_REF_NICHES) } +/// The reference niche policy for builtin types, and for types in +/// crates not specifying `-Z reference-niches`. +const DEFAULT_REF_NICHES: ReferenceNichePolicy = ReferenceNichePolicy { size: false, align: false }; + #[instrument(skip(tcx, query), level = "debug")] fn naive_layout_of<'tcx>( tcx: TyCtxt<'tcx>, @@ -163,7 +165,6 @@ fn naive_layout_of_uncached<'tcx>( // Potentially-wide pointers. ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { let data_ptr = scalar(Pointer(AddressSpace::DATA)); - if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? { // Effectively a (ptr, meta) tuple. data_ptr @@ -322,15 +323,36 @@ fn layout_of_uncached<'tcx>( ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { let mut data_ptr = scalar_unit(Pointer(AddressSpace::DATA)); if !ty.is_unsafe_ptr() { - match cx.naive_layout_of(pointee) { - // TODO(reference_niches): actually use the naive layout to set - // reference niches; the query is still kept to for testing purposes. - Ok(_) => (), + // Calling `layout_of` here would cause a query cycle for recursive types; + // so use a conservative estimate that doesn't look past references. + let naive = match cx.naive_layout_of(pointee) { + Ok(n) => n.layout, // This can happen when computing the `SizeSkeleton` of a generic type. - Err(LayoutError::Unknown(_)) => (), + Err(LayoutError::Unknown(_)) => { + // TODO(reference_niches): this is *very* incorrect, but we can't + // return an error here; this would break transmute checks. + // We need some other solution. + NaiveLayout::EMPTY + } Err(err) => return Err(err), - } - data_ptr.valid_range_mut().start = 1; + }; + + let niches = match *pointee.kind() { + ty::FnDef(def, ..) + | ty::Foreign(def) + | ty::Generator(def, ..) + | ty::Closure(def, ..) => tcx.reference_niches_policy(def.krate), + ty::Adt(def, _) => tcx.reference_niches_policy(def.did().krate), + _ => DEFAULT_REF_NICHES, + }; + + let (min_addr, max_addr) = dl.address_range_for( + if niches.size { naive.min_size } else { Size::ZERO }, + if niches.align { naive.min_align } else { Align::ONE }, + ); + + *data_ptr.valid_range_mut() = + WrappingRange { start: min_addr.into(), end: max_addr.into() }; } if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? { From 76c49aead6d49a993c4b2e59cceaf7d8d3324944 Mon Sep 17 00:00:00 2001 From: Moulins Date: Sun, 25 Jun 2023 19:32:40 +0200 Subject: [PATCH 088/113] support non-null pointer niches in CTFE --- compiler/rustc_abi/src/lib.rs | 37 +++++++++++++ compiler/rustc_const_eval/messages.ftl | 1 - .../src/const_eval/machine.rs | 2 +- compiler/rustc_const_eval/src/errors.rs | 5 +- .../src/interpret/discriminant.rs | 24 +++++---- .../rustc_const_eval/src/interpret/memory.rs | 41 ++++++++------ .../src/interpret/validity.rs | 54 ++++++++----------- .../rustc_middle/src/mir/interpret/error.rs | 1 - 8 files changed, 100 insertions(+), 65 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 0839e95723a8..4c046d55af7a 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1006,6 +1006,43 @@ impl WrappingRange { } } + /// Returns `true` if `range` is contained in `self`. + #[inline(always)] + pub fn contains_range + Ord>(&self, range: RangeInclusive) -> bool { + if range.is_empty() { + return true; + } + + let (vmin, vmax) = range.into_inner(); + let (vmin, vmax) = (vmin.into(), vmax.into()); + + if self.start <= self.end { + self.start <= vmin && vmax <= self.end + } else { + // The last check is needed to cover the following case: + // `vmin ... start, end ... vmax`. In this special case there is no gap + // between `start` and `end` so we must return true. + self.start <= vmin || vmax <= self.end || self.start == self.end + 1 + } + } + + /// Returns `true` if `range` has an overlap with `self`. + #[inline(always)] + pub fn overlaps_range + Ord>(&self, range: RangeInclusive) -> bool { + if range.is_empty() { + return false; + } + + let (vmin, vmax) = range.into_inner(); + let (vmin, vmax) = (vmin.into(), vmax.into()); + + if self.start <= self.end { + self.start <= vmax && vmin <= self.end + } else { + self.start <= vmax || vmin <= self.end + } + } + /// Returns `self` with replaced `start` #[inline(always)] pub fn with_start(mut self, start: u128) -> Self { diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index d8eade5bd2a0..8833f55831ca 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -244,7 +244,6 @@ const_eval_not_enough_caller_args = const_eval_null_box = {$front_matter}: encountered a null box const_eval_null_fn_ptr = {$front_matter}: encountered a null function pointer const_eval_null_ref = {$front_matter}: encountered a null reference -const_eval_nullable_ptr_out_of_range = {$front_matter}: encountered a potentially null pointer, but expected something that cannot possibly fail to be {$in_range} const_eval_nullary_intrinsic_fail = could not evaluate nullary intrinsic diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 267795a6cb4a..51012da6b904 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -333,7 +333,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { // Inequality with integers other than null can never be known for sure. (Scalar::Int(int), ptr @ Scalar::Ptr(..)) | (ptr @ Scalar::Ptr(..), Scalar::Int(int)) - if int.is_null() && !self.scalar_may_be_null(ptr)? => + if int.is_null() && !self.ptr_scalar_range(ptr)?.contains(&0) => { 0 } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index ca38cce710e6..61ce695ccd29 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -617,7 +617,6 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { MutableRefInConst => const_eval_mutable_ref_in_const, NullFnPtr => const_eval_null_fn_ptr, NeverVal => const_eval_never_val, - NullablePtrOutOfRange { .. } => const_eval_nullable_ptr_out_of_range, PtrOutOfRange { .. } => const_eval_ptr_out_of_range, OutOfRange { .. } => const_eval_out_of_range, UnsafeCell => const_eval_unsafe_cell, @@ -732,9 +731,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { | InvalidFnPtr { value } => { err.set_arg("value", value); } - NullablePtrOutOfRange { range, max_value } | PtrOutOfRange { range, max_value } => { - add_range_arg(range, max_value, handler, err) - } + PtrOutOfRange { range, max_value } => add_range_arg(range, max_value, handler, err), OutOfRange { range, max_value, value } => { err.set_arg("value", value); add_range_arg(range, max_value, handler, err); diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index f23a455c2ca3..99ea0ab18bc9 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -2,8 +2,7 @@ use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt}; use rustc_middle::{mir, ty}; -use rustc_target::abi::{self, TagEncoding}; -use rustc_target::abi::{VariantIdx, Variants}; +use rustc_target::abi::{self, TagEncoding, VariantIdx, Variants, WrappingRange}; use super::{ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Scalar}; @@ -180,19 +179,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // discriminant (encoded in niche/tag) and variant index are the same. let variants_start = niche_variants.start().as_u32(); let variants_end = niche_variants.end().as_u32(); + let variants_len = u128::from(variants_end - variants_start); let variant = match tag_val.try_to_int() { Err(dbg_val) => { // So this is a pointer then, and casting to an int failed. // Can only happen during CTFE. - // The niche must be just 0, and the ptr not null, then we know this is - // okay. Everything else, we conservatively reject. - let ptr_valid = niche_start == 0 - && variants_start == variants_end - && !self.scalar_may_be_null(tag_val)?; - if !ptr_valid { + // The pointer and niches ranges must be disjoint, then we know + // this is the untagged variant (as the value is not in the niche). + // Everything else, we conservatively reject. + let range = self.ptr_scalar_range(tag_val)?; + let niches = WrappingRange { + start: niche_start, + end: niche_start.wrapping_add(variants_len), + }; + if niches.overlaps_range(range) { throw_ub!(InvalidTag(dbg_val)) + } else { + untagged_variant } - untagged_variant } Ok(tag_bits) => { let tag_bits = tag_bits.assert_bits(tag_layout.size); @@ -205,7 +209,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let variant_index_relative = variant_index_relative_val.to_scalar().assert_bits(tag_val.layout.size); // Check if this is in the range that indicates an actual discriminant. - if variant_index_relative <= u128::from(variants_end - variants_start) { + if variant_index_relative <= variants_len { let variant_index_relative = u32::try_from(variant_index_relative) .expect("we checked that this fits into a u32"); // Then computing the absolute variant idx should not overflow any more. diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 7b44a20ef03d..10a2a70364b8 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -10,6 +10,7 @@ use std::assert_matches::assert_matches; use std::borrow::Cow; use std::collections::VecDeque; use std::fmt; +use std::ops::RangeInclusive; use std::ptr; use rustc_ast::Mutability; @@ -1222,24 +1223,34 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Machine pointer introspection. impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { - /// Test if this value might be null. + /// Turn a pointer-sized scalar into a (non-empty) range of possible values. /// If the machine does not support ptr-to-int casts, this is conservative. - pub fn scalar_may_be_null(&self, scalar: Scalar) -> InterpResult<'tcx, bool> { - Ok(match scalar.try_to_int() { - Ok(int) => int.is_null(), - Err(_) => { - // Can only happen during CTFE. - let ptr = scalar.to_pointer(self)?; - match self.ptr_try_get_alloc_id(ptr) { - Ok((alloc_id, offset, _)) => { - let (size, _align, _kind) = self.get_alloc_info(alloc_id); - // If the pointer is out-of-bounds, it may be null. - // Note that one-past-the-end (offset == size) is still inbounds, and never null. - offset > size - } - Err(_offset) => bug!("a non-int scalar is always a pointer"), + pub fn ptr_scalar_range( + &self, + scalar: Scalar, + ) -> InterpResult<'tcx, RangeInclusive> { + if let Ok(int) = scalar.to_target_usize(self) { + return Ok(int..=int); + } + + let ptr = scalar.to_pointer(self)?; + + // Can only happen during CTFE. + Ok(match self.ptr_try_get_alloc_id(ptr) { + Ok((alloc_id, offset, _)) => { + let offset = offset.bytes(); + let (size, align, _) = self.get_alloc_info(alloc_id); + let dl = self.data_layout(); + if offset > size.bytes() { + // If the pointer is out-of-bounds, we do not have a + // meaningful range to return. + 0..=dl.max_address() + } else { + let (min, max) = dl.address_range_for(size, align); + (min + offset)..=(max + offset) } } + Err(_offset) => bug!("a non-int scalar is always a pointer"), }) } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 21c655988a0e..108394d224bb 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -19,9 +19,7 @@ use rustc_middle::mir::interpret::{ use rustc_middle::ty; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_span::symbol::{sym, Symbol}; -use rustc_target::abi::{ - Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange, -}; +use rustc_target::abi::{Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants}; use std::hash::Hash; @@ -554,7 +552,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // FIXME: Check if the signature matches } else { // Otherwise (for standalone Miri), we have to still check it to be non-null. - if self.ecx.scalar_may_be_null(value)? { + if self.ecx.ptr_scalar_range(value)?.contains(&0) { throw_validation_failure!(self.path, NullFnPtr); } } @@ -595,46 +593,36 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' ) -> InterpResult<'tcx> { let size = scalar_layout.size(self.ecx); let valid_range = scalar_layout.valid_range(self.ecx); - let WrappingRange { start, end } = valid_range; let max_value = size.unsigned_int_max(); - assert!(end <= max_value); - let bits = match scalar.try_to_int() { - Ok(int) => int.assert_bits(size), + assert!(valid_range.end <= max_value); + match scalar.try_to_int() { + Ok(int) => { + // We have an explicit int: check it against the valid range. + let bits = int.assert_bits(size); + if valid_range.contains(bits) { + Ok(()) + } else { + throw_validation_failure!( + self.path, + OutOfRange { value: format!("{bits}"), range: valid_range, max_value } + ) + } + } Err(_) => { // So this is a pointer then, and casting to an int failed. // Can only happen during CTFE. - // We support 2 kinds of ranges here: full range, and excluding zero. - if start == 1 && end == max_value { - // Only null is the niche. So make sure the ptr is NOT null. - if self.ecx.scalar_may_be_null(scalar)? { - throw_validation_failure!( - self.path, - NullablePtrOutOfRange { range: valid_range, max_value } - ) - } else { - return Ok(()); - } - } else if scalar_layout.is_always_valid(self.ecx) { - // Easy. (This is reachable if `enforce_number_validity` is set.) - return Ok(()); + // We check if the possible addresses are compatible with the valid range. + let range = self.ecx.ptr_scalar_range(scalar)?; + if valid_range.contains_range(range) { + Ok(()) } else { - // Conservatively, we reject, because the pointer *could* have a bad - // value. + // Reject conservatively, because the pointer *could* have a bad value. throw_validation_failure!( self.path, PtrOutOfRange { range: valid_range, max_value } ) } } - }; - // Now compare. - if valid_range.contains(bits) { - Ok(()) - } else { - throw_validation_failure!( - self.path, - OutOfRange { value: format!("{bits}"), range: valid_range, max_value } - ) } } } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 372452ea29a8..1bcef17d73b0 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -388,7 +388,6 @@ pub enum ValidationErrorKind<'tcx> { MutableRefInConst, NullFnPtr, NeverVal, - NullablePtrOutOfRange { range: WrappingRange, max_value: u128 }, PtrOutOfRange { range: WrappingRange, max_value: u128 }, OutOfRange { value: String, range: WrappingRange, max_value: u128 }, UnsafeCell, From 4fb039ed6c069e0ba9ac6215422d4f61a705dc1c Mon Sep 17 00:00:00 2001 From: Moulins Date: Mon, 26 Jun 2023 15:40:13 +0200 Subject: [PATCH 089/113] recover null-ptr optimization by adding a special case to the niching logic --- compiler/rustc_abi/src/lib.rs | 20 ++++++++++++-------- compiler/rustc_middle/src/ty/layout.rs | 1 + 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 4c046d55af7a..3c64e89e3d25 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1503,16 +1503,21 @@ impl Niche { pub fn reserve(&self, cx: &C, count: u128) -> Option<(u128, Scalar)> { assert!(count > 0); + if count > self.available(cx) { + return None; + } let Self { value, valid_range: v, .. } = *self; - let size = value.size(cx); - assert!(size.bits() <= 128); - let max_value = size.unsigned_int_max(); + let max_value = value.size(cx).unsigned_int_max(); + let distance_end_zero = max_value - v.end; - let niche = v.end.wrapping_add(1)..v.start; - let available = niche.end.wrapping_sub(niche.start) & max_value; - if count > available { - return None; + // Null-pointer optimization. This is guaranteed by Rust (at least for `Option<_>`), + // and offers better codegen opportunities. + if count == 1 && matches!(value, Pointer(_)) && !v.contains(0) { + // Select which bound to move to minimize the number of lost niches. + let valid_range = + if v.start - 1 > distance_end_zero { v.with_end(0) } else { v.with_start(0) }; + return Some((0, Scalar::Initialized { value, valid_range })); } // Extend the range of valid values being reserved by moving either `v.start` or `v.end` bound. @@ -1535,7 +1540,6 @@ impl Niche { let end = v.end.wrapping_add(count) & max_value; Some((start, Scalar::Initialized { value, valid_range: v.with_end(end) })) }; - let distance_end_zero = max_value - v.end; if v.start > v.end { // zero is unavailable because wrapping occurs move_end(v) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 31005bdd64a0..328200d40ba0 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1087,6 +1087,7 @@ where // this will continue to work as long as we don't start // using more niches than just null (e.g., the first page of // the address space, or unaligned pointers). + // FIXME(reference_niches): well, the day has come... Variants::Multiple { tag_encoding: TagEncoding::Niche { untagged_variant, .. }, tag_field, From 403f34b599fb3ed7abd71f3388da3c5061a2d84f Mon Sep 17 00:00:00 2001 From: Moulins Date: Tue, 27 Jun 2023 00:47:27 +0200 Subject: [PATCH 090/113] Don't treat ref. fields with non-null niches as `dereferenceable_or_null` --- compiler/rustc_codegen_gcc/src/type_of.rs | 3 +- compiler/rustc_codegen_llvm/src/type_of.rs | 4 +- compiler/rustc_middle/src/ty/layout.rs | 49 ++++++++++++---------- compiler/rustc_target/src/abi/mod.rs | 6 ++- 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 84d578385127..a30bce0a313c 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -339,7 +339,8 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { return pointee; } - let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset); + let assume_valid_ptr = true; + let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset, assume_valid_ptr); cx.pointee_infos.borrow_mut().insert((self.ty, offset), result); result diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 2dbd467cc84c..29dd53ff763a 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -411,8 +411,8 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { if let Some(&pointee) = cx.pointee_infos.borrow().get(&(self.ty, offset)) { return pointee; } - - let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset); + let assume_valid_ptr = true; + let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset, assume_valid_ptr); cx.pointee_infos.borrow_mut().insert((self.ty, offset), result); result diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 328200d40ba0..0aa109225425 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1036,6 +1036,9 @@ where this: TyAndLayout<'tcx>, cx: &C, offset: Size, + // If true, assume that pointers are either null or valid (according to their type), + // enabling extra optimizations. + mut assume_valid_ptr: bool, ) -> Option { let tcx = cx.tcx(); let param_env = cx.param_env(); @@ -1058,19 +1061,19 @@ where // Freeze/Unpin queries, and can save time in the codegen backend (noalias // attributes in LLVM have compile-time cost even in unoptimized builds). let optimize = tcx.sess.opts.optimize != OptLevel::No; - let kind = match mt { - hir::Mutability::Not => PointerKind::SharedRef { + let safe = match (assume_valid_ptr, mt) { + (true, hir::Mutability::Not) => Some(PointerKind::SharedRef { frozen: optimize && ty.is_freeze(tcx, cx.param_env()), - }, - hir::Mutability::Mut => PointerKind::MutableRef { + }), + (true, hir::Mutability::Mut) => Some(PointerKind::MutableRef { unpin: optimize && ty.is_unpin(tcx, cx.param_env()), - }, + }), + (false, _) => None, }; - tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo { size: layout.size, align: layout.align.abi, - safe: Some(kind), + safe, }) } @@ -1079,20 +1082,21 @@ where // Within the discriminant field, only the niche itself is // always initialized, so we only check for a pointer at its // offset. - // - // If the niche is a pointer, it's either valid (according - // to its type), or null (which the niche field's scalar - // validity range encodes). This allows using - // `dereferenceable_or_null` for e.g., `Option<&T>`, and - // this will continue to work as long as we don't start - // using more niches than just null (e.g., the first page of - // the address space, or unaligned pointers). - // FIXME(reference_niches): well, the day has come... Variants::Multiple { - tag_encoding: TagEncoding::Niche { untagged_variant, .. }, + tag_encoding: + TagEncoding::Niche { + untagged_variant, + niche_variants: ref variants, + niche_start, + }, tag_field, .. } if this.fields.offset(tag_field) == offset => { + // We can only continue assuming pointer validity if the only possible + // discriminant value is null. The null special-case is permitted by LLVM's + // `dereferenceable_or_null`, and allow types like `Option<&T>` to benefit + // from optimizations. + assume_valid_ptr &= niche_start == 0 && variants.start() == variants.end(); Some(this.for_variant(cx, untagged_variant)) } _ => Some(this), @@ -1118,9 +1122,12 @@ where result = field.to_result().ok().and_then(|field| { if ptr_end <= field_start + field.size { // We found the right field, look inside it. - let field_info = - field.pointee_info_at(cx, offset - field_start); - field_info + Self::ty_and_layout_pointee_info_at( + field, + cx, + offset - field_start, + assume_valid_ptr, + ) } else { None } @@ -1135,7 +1142,7 @@ where // FIXME(eddyb) This should be for `ptr::Unique`, not `Box`. if let Some(ref mut pointee) = result { if let ty::Adt(def, _) = this.ty.kind() { - if def.is_box() && offset.bytes() == 0 { + if assume_valid_ptr && def.is_box() && offset.bytes() == 0 { let optimize = tcx.sess.opts.optimize != OptLevel::No; pointee.safe = Some(PointerKind::Box { unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()), diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 589cd3cf96b3..752f10a74ac1 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -50,6 +50,9 @@ pub trait TyAbiInterface<'a, C>: Sized { this: TyAndLayout<'a, Self>, cx: &C, offset: Size, + // If true, assume that pointers are either null or valid (according to their type), + // enabling extra optimizations. + assume_valid_ptr: bool, ) -> Option; fn is_adt(this: TyAndLayout<'a, Self>) -> bool; fn is_never(this: TyAndLayout<'a, Self>) -> bool; @@ -76,7 +79,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { where Ty: TyAbiInterface<'a, C>, { - Ty::ty_and_layout_pointee_info_at(self, cx, offset) + let assume_valid_ptr = true; + Ty::ty_and_layout_pointee_info_at(self, cx, offset, assume_valid_ptr) } pub fn is_single_fp_element(self, cx: &C) -> bool From c30fbb95a6ac4af9f08628870fd5b8cad6ccbf75 Mon Sep 17 00:00:00 2001 From: Moulins Date: Wed, 28 Jun 2023 09:02:24 +0200 Subject: [PATCH 091/113] Track exactness in `NaiveLayout` and use it for `SizeSkeleton` checks --- compiler/rustc_middle/src/ty/layout.rs | 36 ++++++++-- compiler/rustc_ty_utils/src/layout.rs | 71 ++++++++++--------- tests/ui/lint/invalid_value.stderr | 6 +- .../issue-53092-2.stderr | 1 - 4 files changed, 70 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 0aa109225425..5cf91916129a 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -15,7 +15,7 @@ use rustc_target::abi::call::FnAbi; use rustc_target::abi::*; use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target}; -use std::cmp; +use std::cmp::{self, Ordering}; use std::fmt; use std::num::NonZeroUsize; use std::ops::Bound; @@ -313,7 +313,16 @@ impl<'tcx> SizeSkeleton<'tcx> { ) -> Result, &'tcx LayoutError<'tcx>> { debug_assert!(!ty.has_non_region_infer()); - // First try computing a static layout. + // First, try computing an exact naive layout (this covers simple types with generic + // references, where a full static layout would fail). + if let Ok(layout) = tcx.naive_layout_of(param_env.and(ty)) { + if layout.is_exact { + return Ok(SizeSkeleton::Known(layout.min_size)); + } + } + + // Second, try computing a full static layout (this covers cases when the naive layout + // wasn't smart enough, but cannot deal with generic references). let err = match tcx.layout_of(param_env.and(ty)) { Ok(layout) => { return Ok(SizeSkeleton::Known(layout.size)); @@ -327,6 +336,7 @@ impl<'tcx> SizeSkeleton<'tcx> { ) => return Err(e), }; + // Third, fall back to ad-hoc cases. match *ty.kind() { ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { let non_zero = !ty.is_unsafe_ptr(); @@ -645,18 +655,28 @@ impl std::ops::DerefMut for TyAndNaiveLayout<'_> { pub struct NaiveLayout { pub min_size: Size, pub min_align: Align, + // If `true`, `min_size` and `min_align` are guaranteed to be exact. + pub is_exact: bool, } impl NaiveLayout { - pub const EMPTY: Self = Self { min_size: Size::ZERO, min_align: Align::ONE }; + pub const UNKNOWN: Self = Self { min_size: Size::ZERO, min_align: Align::ONE, is_exact: false }; + pub const EMPTY: Self = Self { min_size: Size::ZERO, min_align: Align::ONE, is_exact: true }; - pub fn is_underestimate_of(&self, layout: Layout<'_>) -> bool { - self.min_size <= layout.size() && self.min_align <= layout.align().abi + pub fn is_compatible_with(&self, layout: Layout<'_>) -> bool { + let cmp = |cmp: Ordering| match (cmp, self.is_exact) { + (Ordering::Less | Ordering::Equal, false) => true, + (Ordering::Equal, true) => true, + (_, _) => false, + }; + + cmp(self.min_size.cmp(&layout.size())) && cmp(self.min_align.cmp(&layout.align().abi)) } #[must_use] - pub fn pad_to_align(self) -> Self { - Self { min_size: self.min_size.align_to(self.min_align), min_align: self.min_align } + pub fn pad_to_align(mut self) -> Self { + self.min_size = self.min_size.align_to(self.min_align); + self } #[must_use] @@ -664,6 +684,7 @@ impl NaiveLayout { Some(Self { min_size: self.min_size.checked_add(other.min_size, cx)?, min_align: std::cmp::max(self.min_align, other.min_align), + is_exact: self.is_exact && other.is_exact, }) } @@ -672,6 +693,7 @@ impl NaiveLayout { Self { min_size: std::cmp::max(self.min_size, other.min_size), min_align: std::cmp::max(self.min_align, other.min_align), + is_exact: self.is_exact && other.is_exact, } } } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index c81e76e44716..d484448a0fec 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -90,9 +90,9 @@ fn layout_of<'tcx>( let cx = LayoutCx { tcx, param_env }; let layout = layout_of_uncached(&cx, ty)?; - if !naive.is_underestimate_of(layout) { + if !naive.is_compatible_with(layout) { bug!( - "the estimated naive layout is bigger than the actual layout:\n{:#?}\n{:#?}", + "the naive layout isn't compatible with the actual layout:\n{:#?}\n{:#?}", naive, layout, ); @@ -119,15 +119,23 @@ fn naive_layout_of_uncached<'tcx>( let tcx = cx.tcx; let dl = cx.data_layout(); - let scalar = - |value: Primitive| NaiveLayout { min_size: value.size(dl), min_align: value.align(dl).abi }; + let scalar = |value: Primitive| NaiveLayout { + min_size: value.size(dl), + min_align: value.align(dl).abi, + is_exact: true, + }; let univariant = |fields: &mut dyn Iterator>, repr: &ReprOptions| -> Result> { + if repr.pack.is_some() && repr.align.is_some() { + cx.tcx.sess.delay_span_bug(DUMMY_SP, "struct cannot be packed and aligned"); + return Err(error(cx, LayoutError::Unknown(ty))); + } + // For simplicity, ignore inter-field padding; this may underestimate the size. // FIXME(reference_niches): Be smarter and implement something closer to the real layout logic. - let mut layout = NaiveLayout::EMPTY; + let mut layout = NaiveLayout::UNKNOWN; for field in fields { let field = cx.naive_layout_of(field)?; layout = layout @@ -192,12 +200,14 @@ fn naive_layout_of_uncached<'tcx>( .min_size .checked_mul(count, cx) .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?, - min_align: element.min_align, + ..*element } } - ty::Slice(element) => { - NaiveLayout { min_size: Size::ZERO, min_align: cx.naive_layout_of(element)?.min_align } - } + ty::Slice(element) => NaiveLayout { + min_size: Size::ZERO, + // NOTE: this could be unconditionally exact if `NaiveLayout` guaranteed exact align. + ..*cx.naive_layout_of(element)? + }, ty::Str => NaiveLayout::EMPTY, // Odd unit types. @@ -205,7 +215,7 @@ fn naive_layout_of_uncached<'tcx>( // FIXME(reference_niches): try to actually compute a reasonable layout estimate, // without duplicating too much code from `generator_layout`. - ty::Generator(..) => NaiveLayout::EMPTY, + ty::Generator(..) => NaiveLayout::UNKNOWN, ty::Closure(_, ref substs) => { univariant(&mut substs.as_closure().upvar_tys(), &ReprOptions::default())? @@ -223,12 +233,21 @@ fn naive_layout_of_uncached<'tcx>( } ty::Adt(def, substs) => { - // For simplicity, assume that any discriminant field (if it exists) - // gets niched inside one of the variants; this will underestimate the size - // (and sometimes alignment) of enums. - // FIXME(reference_niches): Be smarter and actually take into accoount the discriminant. let repr = def.repr(); - def.variants().iter().try_fold(NaiveLayout::EMPTY, |layout, v| { + let base = if def.is_struct() && !repr.simd() { + // FIXME(reference_niches): compute proper alignment for SIMD types. + NaiveLayout::EMPTY + } else { + // For simplicity, assume that any discriminant field (if it exists) + // gets niched inside one of the variants; this will underestimate the size + // (and sometimes alignment) of enums. + // FIXME(reference_niches): Be smarter and actually take into accoount the discriminant. + // Also consider adding a special case for null-optimized enums, so that we can have + // `Option<&T>: PointerLike` in generic contexts. + NaiveLayout::UNKNOWN + }; + + def.variants().iter().try_fold(base, |layout, v| { let mut fields = v.fields.iter().map(|f| f.ty(tcx, substs)); let vlayout = univariant(&mut fields, &repr)?; Ok(layout.union(&vlayout)) @@ -260,12 +279,10 @@ fn univariant_uninterned<'tcx>( kind: StructKind, ) -> Result> { let dl = cx.data_layout(); - let pack = repr.pack; - if pack.is_some() && repr.align.is_some() { - cx.tcx.sess.delay_span_bug(DUMMY_SP, "struct cannot be packed and aligned"); - return Err(cx.tcx.arena.alloc(LayoutError::Unknown(ty))); - } - + assert!( + !(repr.pack.is_some() && repr.align.is_some()), + "already rejected by `naive_layout_of`" + ); cx.univariant(dl, fields, repr, kind).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty))) } @@ -325,17 +342,7 @@ fn layout_of_uncached<'tcx>( if !ty.is_unsafe_ptr() { // Calling `layout_of` here would cause a query cycle for recursive types; // so use a conservative estimate that doesn't look past references. - let naive = match cx.naive_layout_of(pointee) { - Ok(n) => n.layout, - // This can happen when computing the `SizeSkeleton` of a generic type. - Err(LayoutError::Unknown(_)) => { - // TODO(reference_niches): this is *very* incorrect, but we can't - // return an error here; this would break transmute checks. - // We need some other solution. - NaiveLayout::EMPTY - } - Err(err) => return Err(err), - }; + let naive = cx.naive_layout_of(pointee)?.layout; let niches = match *pointee.kind() { ty::FnDef(def, ..) diff --git a/tests/ui/lint/invalid_value.stderr b/tests/ui/lint/invalid_value.stderr index 57531b0968f1..066fdccbaadf 100644 --- a/tests/ui/lint/invalid_value.stderr +++ b/tests/ui/lint/invalid_value.stderr @@ -34,8 +34,7 @@ LL | let _val: Wrap<&'static T> = mem::zeroed(); | this code causes undefined behavior when executed | help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done | - = note: `Wrap<&T>` must be non-null -note: because references must be non-null (in this struct field) +note: references must be non-null (in this struct field) --> $DIR/invalid_value.rs:17:18 | LL | struct Wrap { wrapped: T } @@ -50,8 +49,7 @@ LL | let _val: Wrap<&'static T> = mem::uninitialized(); | this code causes undefined behavior when executed | help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done | - = note: `Wrap<&T>` must be non-null -note: because references must be non-null (in this struct field) +note: references must be non-null (in this struct field) --> $DIR/invalid_value.rs:17:18 | LL | struct Wrap { wrapped: T } diff --git a/tests/ui/type-alias-impl-trait/issue-53092-2.stderr b/tests/ui/type-alias-impl-trait/issue-53092-2.stderr index dd6793242807..9d90c6fbc58e 100644 --- a/tests/ui/type-alias-impl-trait/issue-53092-2.stderr +++ b/tests/ui/type-alias-impl-trait/issue-53092-2.stderr @@ -9,7 +9,6 @@ note: ...which requires type-checking `CONST_BUG`... | LL | const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing layout of `Bug`... = note: ...which requires computing layout (naive) of `Bug`... = note: ...which requires normalizing `Bug`... = note: ...which again requires computing type of `Bug::{opaque#0}`, completing the cycle From feb20f2fe77143c8905de3c34ce41ff42976628c Mon Sep 17 00:00:00 2001 From: Moulins Date: Thu, 29 Jun 2023 03:55:09 +0200 Subject: [PATCH 092/113] Track ABI info. in `NaiveLayout`, and use it for `PointerLike` checks THis significantly complicates `NaiveLayout` logic, but is necessary to ensure that bounds like `NonNull: PointerLike` hold in generic contexts. Also implement exact layout computation for structs. --- compiler/rustc_middle/src/ty/layout.rs | 155 ++++++++++++++---- .../src/solve/trait_goals.rs | 17 +- .../src/traits/select/candidate_assembly.rs | 17 +- compiler/rustc_ty_utils/src/layout.rs | 134 ++++++++------- .../param-env-region-infer.next.stderr | 1 - 5 files changed, 231 insertions(+), 93 deletions(-) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 5cf91916129a..f9e55704caf5 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -15,7 +15,7 @@ use rustc_target::abi::call::FnAbi; use rustc_target::abi::*; use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target}; -use std::cmp::{self, Ordering}; +use std::cmp; use std::fmt; use std::num::NonZeroUsize; use std::ops::Bound; @@ -316,8 +316,8 @@ impl<'tcx> SizeSkeleton<'tcx> { // First, try computing an exact naive layout (this covers simple types with generic // references, where a full static layout would fail). if let Ok(layout) = tcx.naive_layout_of(param_env.and(ty)) { - if layout.is_exact { - return Ok(SizeSkeleton::Known(layout.min_size)); + if layout.exact { + return Ok(SizeSkeleton::Known(layout.size)); } } @@ -650,51 +650,146 @@ impl std::ops::DerefMut for TyAndNaiveLayout<'_> { } } -/// A naive underestimation of the layout of a type. +/// Extremely simplified representation of a type's layout. +/// +/// #[derive(Copy, Clone, Debug, HashStable)] pub struct NaiveLayout { - pub min_size: Size, - pub min_align: Align, - // If `true`, `min_size` and `min_align` are guaranteed to be exact. - pub is_exact: bool, + pub abi: NaiveAbi, + pub size: Size, + pub align: Align, + /// If `true`, `size` and `align` are exact. + pub exact: bool, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)] +pub enum NaiveAbi { + /// A scalar layout, always implies `exact`. + Scalar(Primitive), + /// An uninhabited layout. (needed to properly track `Scalar`) + Uninhabited, + /// An unsized aggregate. (needed to properly track `Scalar`) + Unsized, + Any, +} + +impl NaiveAbi { + #[inline] + pub fn as_aggregate(self) -> Self { + match self { + NaiveAbi::Scalar(_) => NaiveAbi::Any, + _ => self, + } + } } impl NaiveLayout { - pub const UNKNOWN: Self = Self { min_size: Size::ZERO, min_align: Align::ONE, is_exact: false }; - pub const EMPTY: Self = Self { min_size: Size::ZERO, min_align: Align::ONE, is_exact: true }; + pub const EMPTY: Self = + Self { size: Size::ZERO, align: Align::ONE, exact: true, abi: NaiveAbi::Any }; - pub fn is_compatible_with(&self, layout: Layout<'_>) -> bool { - let cmp = |cmp: Ordering| match (cmp, self.is_exact) { - (Ordering::Less | Ordering::Equal, false) => true, - (Ordering::Equal, true) => true, - (_, _) => false, - }; + pub fn is_refined_by(&self, layout: Layout<'_>) -> bool { + if self.size > layout.size() || self.align > layout.align().abi { + return false; + } - cmp(self.min_size.cmp(&layout.size())) && cmp(self.min_align.cmp(&layout.align().abi)) + if let NaiveAbi::Scalar(prim) = self.abi { + assert!(self.exact); + if !matches!(layout.abi(), Abi::Scalar(s) if s.primitive() == prim) { + return false; + } + } + + !self.exact || (self.size, self.align) == (layout.size(), layout.align().abi) + } + + /// Returns if this layout is known to be pointer-like (`None` if uncertain) + /// + /// See the corresponding `Layout::is_pointer_like` method. + pub fn is_pointer_like(&self, dl: &TargetDataLayout) -> Option { + match self.abi { + NaiveAbi::Scalar(_) => { + assert!(self.exact); + Some(self.size == dl.pointer_size && self.align == dl.pointer_align.abi) + } + NaiveAbi::Uninhabited | NaiveAbi::Unsized => Some(false), + NaiveAbi::Any if self.exact => Some(false), + NaiveAbi::Any => None, + } } #[must_use] - pub fn pad_to_align(mut self) -> Self { - self.min_size = self.min_size.align_to(self.min_align); + #[inline] + pub fn packed(mut self, align: Align) -> Self { + if self.align > align { + self.align = align; + self.abi = self.abi.as_aggregate(); + } self } #[must_use] - pub fn concat(&self, other: &Self, cx: &C) -> Option { - Some(Self { - min_size: self.min_size.checked_add(other.min_size, cx)?, - min_align: std::cmp::max(self.min_align, other.min_align), - is_exact: self.is_exact && other.is_exact, - }) + #[inline] + pub fn align_to(mut self, align: Align) -> Self { + if align > self.align { + self.align = align; + self.abi = self.abi.as_aggregate(); + } + self } #[must_use] - pub fn union(&self, other: &Self) -> Self { - Self { - min_size: std::cmp::max(self.min_size, other.min_size), - min_align: std::cmp::max(self.min_align, other.min_align), - is_exact: self.is_exact && other.is_exact, + #[inline] + pub fn pad_to_align(mut self, align: Align) -> Self { + let new_size = self.size.align_to(align); + if new_size > self.size { + self.abi = self.abi.as_aggregate(); + self.size = new_size; } + self + } + + #[must_use] + #[inline] + pub fn concat(&self, other: &Self, dl: &TargetDataLayout) -> Option { + use NaiveAbi::*; + + let size = self.size.checked_add(other.size, dl)?; + let align = cmp::max(self.align, other.align); + let exact = self.exact && other.exact; + let abi = match (self.abi, other.abi) { + // The uninhabited and unsized ABIs override everything. + (Uninhabited, _) | (_, Uninhabited) => Uninhabited, + (Unsized, _) | (_, Unsized) => Unsized, + // A scalar struct must have a single non ZST-field. + (_, s @ Scalar(_)) if exact && self.size == Size::ZERO => s, + (s @ Scalar(_), _) if exact && other.size == Size::ZERO => s, + // Default case. + (_, _) => Any, + }; + Some(Self { abi, size, align, exact }) + } + + #[must_use] + #[inline] + pub fn union(&self, other: &Self) -> Self { + use NaiveAbi::*; + + let size = cmp::max(self.size, other.size); + let align = cmp::max(self.align, other.align); + let exact = self.exact && other.exact; + let abi = match (self.abi, other.abi) { + // The unsized ABI overrides everything. + (Unsized, _) | (_, Unsized) => Unsized, + // A scalar union must have a single non ZST-field. + (_, s @ Scalar(_)) if exact && self.size == Size::ZERO => s, + (s @ Scalar(_), _) if exact && other.size == Size::ZERO => s, + // ...or identical scalar fields. + (Scalar(s1), Scalar(s2)) if s1 == s2 => Scalar(s1), + // Default cases. + (Uninhabited, Uninhabited) => Uninhabited, + (_, _) => Any, + }; + Self { abi, size, align, exact } } } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 930e62d63883..761f5327f6db 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -223,9 +223,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); } - if let Ok(layout) = tcx.layout_of(key) - && layout.layout.is_pointer_like(&tcx.data_layout) - { + // First, try computing an exact naive layout in case the type is generic. + let is_pointer_like = if let Ok(layout) = tcx.naive_layout_of(key) { + layout.is_pointer_like(&tcx.data_layout).unwrap_or_else(|| { + // Second, we fall back to full layout computation. + tcx.layout_of(key) + .ok() + .filter(|l| l.layout.is_pointer_like(&tcx.data_layout)) + .is_some() + }) + } else { + false + }; + + if is_pointer_like { // FIXME: We could make this faster by making a no-constraints response ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { 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 aa195d70a9f6..f1d870269a60 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -979,9 +979,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; } - if let Ok(layout) = tcx.layout_of(key) - && layout.layout.is_pointer_like(&tcx.data_layout) - { + // First, try computing an exact naive layout in case the type is generic. + let is_pointer_like = if let Ok(layout) = tcx.naive_layout_of(key) { + layout.is_pointer_like(&tcx.data_layout).unwrap_or_else(|| { + // Second, we fall back to full layout computation. + tcx.layout_of(key) + .ok() + .filter(|l| l.layout.is_pointer_like(&tcx.data_layout)) + .is_some() + }) + } else { + false + }; + + if is_pointer_like { candidates.vec.push(BuiltinCandidate { has_nested: false }); } } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index d484448a0fec..26960a71c70e 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -5,8 +5,8 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::{GeneratorLayout, GeneratorSavedLocal}; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::layout::{ - IntegerExt, LayoutCx, LayoutError, LayoutOf, NaiveLayout, TyAndLayout, TyAndNaiveLayout, - MAX_SIMD_LANES, + IntegerExt, LayoutCx, LayoutError, LayoutOf, NaiveAbi, NaiveLayout, TyAndLayout, + TyAndNaiveLayout, MAX_SIMD_LANES, }; use rustc_middle::ty::{ self, AdtDef, EarlyBinder, GenericArgsRef, ReprOptions, Ty, TyCtxt, TypeVisitableExt, @@ -90,12 +90,8 @@ fn layout_of<'tcx>( let cx = LayoutCx { tcx, param_env }; let layout = layout_of_uncached(&cx, ty)?; - if !naive.is_compatible_with(layout) { - bug!( - "the naive layout isn't compatible with the actual layout:\n{:#?}\n{:#?}", - naive, - layout, - ); + if !naive.is_refined_by(layout) { + bug!("the naive layout isn't refined by the actual layout:\n{:#?}\n{:#?}", naive, layout,); } let layout = TyAndLayout { ty, layout }; @@ -120,9 +116,10 @@ fn naive_layout_of_uncached<'tcx>( let dl = cx.data_layout(); let scalar = |value: Primitive| NaiveLayout { - min_size: value.size(dl), - min_align: value.align(dl).abi, - is_exact: true, + abi: NaiveAbi::Scalar(value), + size: value.size(dl), + align: value.align(dl).abi, + exact: true, }; let univariant = |fields: &mut dyn Iterator>, @@ -133,24 +130,29 @@ fn naive_layout_of_uncached<'tcx>( return Err(error(cx, LayoutError::Unknown(ty))); } - // For simplicity, ignore inter-field padding; this may underestimate the size. - // FIXME(reference_niches): Be smarter and implement something closer to the real layout logic. - let mut layout = NaiveLayout::UNKNOWN; + let linear = repr.inhibit_struct_field_reordering_opt(); + let pack = repr.pack.unwrap_or(Align::MAX); + let mut layout = NaiveLayout::EMPTY; + for field in fields { - let field = cx.naive_layout_of(field)?; + let field = cx.naive_layout_of(field)?.packed(pack); + if linear { + layout = layout.pad_to_align(field.align); + } layout = layout - .concat(&field, cx) + .concat(&field, dl) .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?; } if let Some(align) = repr.align { - layout.min_align = std::cmp::max(layout.min_align, align); - } - if let Some(pack) = repr.pack { - layout.min_align = std::cmp::min(layout.min_align, pack); + layout = layout.align_to(align); } - Ok(layout.pad_to_align()) + if linear { + layout.abi = layout.abi.as_aggregate(); + } + + Ok(layout.pad_to_align(layout.align)) }; debug_assert!(!ty.has_non_region_infer()); @@ -168,17 +170,17 @@ fn naive_layout_of_uncached<'tcx>( ty::FnPtr(_) => scalar(Pointer(dl.instruction_address_space)), // The never type. - ty::Never => NaiveLayout::EMPTY, + ty::Never => NaiveLayout { abi: NaiveAbi::Uninhabited, ..NaiveLayout::EMPTY }, // Potentially-wide pointers. ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { let data_ptr = scalar(Pointer(AddressSpace::DATA)); if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? { // Effectively a (ptr, meta) tuple. - data_ptr - .concat(&scalar(metadata.primitive()), cx) - .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))? - .pad_to_align() + let l = data_ptr + .concat(&scalar(metadata.primitive()), dl) + .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?; + l.pad_to_align(l.align) } else { // No metadata, this is a thin pointer. data_ptr @@ -187,7 +189,7 @@ fn naive_layout_of_uncached<'tcx>( ty::Dynamic(_, _, ty::DynStar) => { let ptr = scalar(Pointer(AddressSpace::DATA)); - ptr.concat(&ptr, cx).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))? + ptr.concat(&ptr, dl).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))? } // Arrays and slices. @@ -196,26 +198,29 @@ fn naive_layout_of_uncached<'tcx>( .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; let element = cx.naive_layout_of(element)?; NaiveLayout { - min_size: element - .min_size + abi: element.abi.as_aggregate(), + size: element + .size .checked_mul(count, cx) .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?, ..*element } } - ty::Slice(element) => NaiveLayout { - min_size: Size::ZERO, - // NOTE: this could be unconditionally exact if `NaiveLayout` guaranteed exact align. - ..*cx.naive_layout_of(element)? - }, - ty::Str => NaiveLayout::EMPTY, + ty::Slice(element) => { + let element = cx.naive_layout_of(element)?; + NaiveLayout { abi: NaiveAbi::Unsized, size: Size::ZERO, ..*element } + } - // Odd unit types. - ty::FnDef(..) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => NaiveLayout::EMPTY, + ty::FnDef(..) => NaiveLayout::EMPTY, + + // Unsized types. + ty::Str | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => { + NaiveLayout { abi: NaiveAbi::Unsized, ..NaiveLayout::EMPTY } + } // FIXME(reference_niches): try to actually compute a reasonable layout estimate, // without duplicating too much code from `generator_layout`. - ty::Generator(..) => NaiveLayout::UNKNOWN, + ty::Generator(..) => NaiveLayout { exact: false, ..NaiveLayout::EMPTY }, ty::Closure(_, ref substs) => { univariant(&mut substs.as_closure().upvar_tys(), &ReprOptions::default())? @@ -225,33 +230,50 @@ fn naive_layout_of_uncached<'tcx>( ty::Adt(def, substs) if def.is_union() => { let repr = def.repr(); - let only_variant = &def.variants()[FIRST_VARIANT]; - only_variant.fields.iter().try_fold(NaiveLayout::EMPTY, |layout, f| { - let mut fields = std::iter::once(f.ty(tcx, substs)); - univariant(&mut fields, &repr).map(|l| layout.union(&l)) - })? + let pack = repr.pack.unwrap_or(Align::MAX); + if repr.pack.is_some() && repr.align.is_some() { + cx.tcx.sess.delay_span_bug(DUMMY_SP, "union cannot be packed and aligned"); + return Err(error(cx, LayoutError::Unknown(ty))); + } + + let mut layout = NaiveLayout::EMPTY; + for f in &def.variants()[FIRST_VARIANT].fields { + let field = cx.naive_layout_of(f.ty(tcx, substs))?; + layout = layout.union(&field.packed(pack)); + } + + // Unions are always inhabited, and never scalar if `repr(C)`. + if !matches!(layout.abi, NaiveAbi::Scalar(_)) || repr.inhibit_enum_layout_opt() { + layout.abi = NaiveAbi::Any; + } + + if let Some(align) = repr.align { + layout = layout.align_to(align); + } + layout.pad_to_align(layout.align) } ty::Adt(def, substs) => { let repr = def.repr(); - let base = if def.is_struct() && !repr.simd() { - // FIXME(reference_niches): compute proper alignment for SIMD types. - NaiveLayout::EMPTY - } else { - // For simplicity, assume that any discriminant field (if it exists) - // gets niched inside one of the variants; this will underestimate the size - // (and sometimes alignment) of enums. - // FIXME(reference_niches): Be smarter and actually take into accoount the discriminant. + let base = NaiveLayout { + // For simplicity, assume that any enum has its discriminant field (if it exists) + // niched inside one of the variants; this will underestimate the size (and sometimes + // alignment) of enums. We also doesn't compute exact alignment for SIMD structs. + // FIXME(reference_niches): Be smarter here. // Also consider adding a special case for null-optimized enums, so that we can have // `Option<&T>: PointerLike` in generic contexts. - NaiveLayout::UNKNOWN + exact: !def.is_enum() && !repr.simd(), + // An ADT with no inhabited variants should have an uninhabited ABI. + abi: NaiveAbi::Uninhabited, + ..NaiveLayout::EMPTY }; - def.variants().iter().try_fold(base, |layout, v| { + let layout = def.variants().iter().try_fold(base, |layout, v| { let mut fields = v.fields.iter().map(|f| f.ty(tcx, substs)); let vlayout = univariant(&mut fields, &repr)?; Ok(layout.union(&vlayout)) - })? + })?; + layout.pad_to_align(layout.align) } // Types with no meaningful known layout. @@ -354,8 +376,8 @@ fn layout_of_uncached<'tcx>( }; let (min_addr, max_addr) = dl.address_range_for( - if niches.size { naive.min_size } else { Size::ZERO }, - if niches.align { naive.min_align } else { Align::ONE }, + if niches.size { naive.size } else { Size::ZERO }, + if niches.align { naive.align } else { Align::ONE }, ); *data_ptr.valid_range_mut() = diff --git a/tests/ui/dyn-star/param-env-region-infer.next.stderr b/tests/ui/dyn-star/param-env-region-infer.next.stderr index d86405462f42..51df71a373ed 100644 --- a/tests/ui/dyn-star/param-env-region-infer.next.stderr +++ b/tests/ui/dyn-star/param-env-region-infer.next.stderr @@ -9,7 +9,6 @@ note: ...which requires type-checking `make_dyn_star`... | LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing layout of `make_dyn_star::{opaque#0}`... = note: ...which requires computing layout (naive) of `make_dyn_star::{opaque#0}`... = note: ...which requires normalizing `make_dyn_star::{opaque#0}`... = note: ...which again requires computing type of `make_dyn_star::{opaque#0}`, completing the cycle From 8e28729a8200e8bc3f625418a724123029c4e215 Mon Sep 17 00:00:00 2001 From: Moulins Date: Sat, 1 Jul 2023 22:50:27 +0200 Subject: [PATCH 093/113] Add doc-comments for `NaiveLayout` --- compiler/rustc_middle/src/query/mod.rs | 5 ++-- compiler/rustc_middle/src/ty/layout.rs | 38 ++++++++++++++++++-------- compiler/rustc_ty_utils/src/layout.rs | 2 +- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 51409f908431..b5b00b7b640f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1394,10 +1394,11 @@ rustc_queries! { desc { "computing layout of `{}`", key.value } } - /// Computes the naive layout estimate of a type. Note that this implicitly + /// Computes the naive layout approximation of a type. Note that this implicitly /// executes in "reveal all" mode, and will normalize the input type. /// - /// Unlike `layout_of`, this doesn't recurse behind reference types. + /// Unlike `layout_of`, this doesn't look past references (beyond the `Pointee::Metadata` + /// projection), and as such can be called on generic types like `Option<&T>`. query naive_layout_of( key: ty::ParamEnvAnd<'tcx, Ty<'tcx>> ) -> Result, &'tcx ty::layout::LayoutError<'tcx>> { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index f9e55704caf5..34a345aba047 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -650,15 +650,16 @@ impl std::ops::DerefMut for TyAndNaiveLayout<'_> { } } -/// Extremely simplified representation of a type's layout. -/// -/// +/// Extremely simplified approximation of a type's layout returned by the +/// `naive_layout_of` query. #[derive(Copy, Clone, Debug, HashStable)] pub struct NaiveLayout { pub abi: NaiveAbi, + /// An underestimate of the layout's size. pub size: Size, + /// An underestimate of the layout's required alignment. pub align: Align, - /// If `true`, `size` and `align` are exact. + /// If `true`, `size` and `align` must be exact values. pub exact: bool, } @@ -670,23 +671,28 @@ pub enum NaiveAbi { Uninhabited, /// An unsized aggregate. (needed to properly track `Scalar`) Unsized, - Any, + /// Any other sized layout. + Sized, } impl NaiveAbi { #[inline] pub fn as_aggregate(self) -> Self { match self { - NaiveAbi::Scalar(_) => NaiveAbi::Any, + NaiveAbi::Scalar(_) => NaiveAbi::Sized, _ => self, } } } impl NaiveLayout { + /// The layout of an empty aggregate, e.g. `()`. pub const EMPTY: Self = - Self { size: Size::ZERO, align: Align::ONE, exact: true, abi: NaiveAbi::Any }; + Self { size: Size::ZERO, align: Align::ONE, exact: true, abi: NaiveAbi::Sized }; + /// Returns whether `self` is a valid approximation of the given full `layout`. + /// + /// This should always return `true` when both layouts are computed from the same type. pub fn is_refined_by(&self, layout: Layout<'_>) -> bool { if self.size > layout.size() || self.align > layout.align().abi { return false; @@ -712,11 +718,12 @@ impl NaiveLayout { Some(self.size == dl.pointer_size && self.align == dl.pointer_align.abi) } NaiveAbi::Uninhabited | NaiveAbi::Unsized => Some(false), - NaiveAbi::Any if self.exact => Some(false), - NaiveAbi::Any => None, + NaiveAbi::Sized if self.exact => Some(false), + NaiveAbi::Sized => None, } } + /// Artificially lowers the alignment of this layout. #[must_use] #[inline] pub fn packed(mut self, align: Align) -> Self { @@ -727,6 +734,7 @@ impl NaiveLayout { self } + /// Artificially raises the alignment of this layout. #[must_use] #[inline] pub fn align_to(mut self, align: Align) -> Self { @@ -737,6 +745,7 @@ impl NaiveLayout { self } + /// Pads this layout so that its size is a multiple of `align`. #[must_use] #[inline] pub fn pad_to_align(mut self, align: Align) -> Self { @@ -748,6 +757,8 @@ impl NaiveLayout { self } + /// Returns the layout of `self` immediately followed by `other`, without any + /// padding between them, as in a packed `struct` or tuple. #[must_use] #[inline] pub fn concat(&self, other: &Self, dl: &TargetDataLayout) -> Option { @@ -764,11 +775,13 @@ impl NaiveLayout { (_, s @ Scalar(_)) if exact && self.size == Size::ZERO => s, (s @ Scalar(_), _) if exact && other.size == Size::ZERO => s, // Default case. - (_, _) => Any, + (_, _) => Sized, }; Some(Self { abi, size, align, exact }) } + /// Returns the layout of `self` superposed with `other`, as in an `enum` + /// or an `union`. #[must_use] #[inline] pub fn union(&self, other: &Self) -> Self { @@ -787,7 +800,7 @@ impl NaiveLayout { (Scalar(s1), Scalar(s2)) if s1 == s2 => Scalar(s1), // Default cases. (Uninhabited, Uninhabited) => Uninhabited, - (_, _) => Any, + (_, _) => Sized, }; Self { abi, size, align, exact } } @@ -849,7 +862,8 @@ pub trait LayoutOf<'tcx>: LayoutOfHelpers<'tcx> { /// Computes the naive layout estimate of a type. Note that this implicitly /// executes in "reveal all" mode, and will normalize the input type. /// - /// Unlike `layout_of`, this doesn't recurse behind reference types. + /// Unlike `layout_of`, this doesn't look past references (beyond the `Pointee::Metadata` + /// projection), and as such can be called on generic types like `Option<&T>`. #[inline] fn naive_layout_of( &self, diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 26960a71c70e..8c484e95491d 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -244,7 +244,7 @@ fn naive_layout_of_uncached<'tcx>( // Unions are always inhabited, and never scalar if `repr(C)`. if !matches!(layout.abi, NaiveAbi::Scalar(_)) || repr.inhibit_enum_layout_opt() { - layout.abi = NaiveAbi::Any; + layout.abi = NaiveAbi::Sized; } if let Some(align) = repr.align { From bf2f8ff2ec5c0b54480d4908ff888a5e650d7dc5 Mon Sep 17 00:00:00 2001 From: Moulins Date: Sat, 1 Jul 2023 23:32:14 +0200 Subject: [PATCH 094/113] Move `naive_layout_of` query provider in separate sibling module --- compiler/rustc_ty_utils/src/layout.rs | 234 +---------------- compiler/rustc_ty_utils/src/layout_naive.rs | 240 ++++++++++++++++++ .../rustc_ty_utils/src/layout_sanity_check.rs | 7 +- compiler/rustc_ty_utils/src/lib.rs | 2 + 4 files changed, 253 insertions(+), 230 deletions(-) create mode 100644 compiler/rustc_ty_utils/src/layout_naive.rs diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 8c484e95491d..da1eba68d53a 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -5,8 +5,7 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::{GeneratorLayout, GeneratorSavedLocal}; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::layout::{ - IntegerExt, LayoutCx, LayoutError, LayoutOf, NaiveAbi, NaiveLayout, TyAndLayout, - TyAndNaiveLayout, MAX_SIMD_LANES, + IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES, }; use rustc_middle::ty::{ self, AdtDef, EarlyBinder, GenericArgsRef, ReprOptions, Ty, TyCtxt, TypeVisitableExt, @@ -25,7 +24,7 @@ use crate::errors::{ use crate::layout_sanity_check::sanity_check_layout; pub fn provide(providers: &mut Providers) { - *providers = Providers { layout_of, naive_layout_of, reference_niches_policy, ..*providers }; + *providers = Providers { layout_of, reference_niches_policy, ..*providers }; } #[instrument(skip(tcx), level = "debug")] @@ -37,40 +36,6 @@ fn reference_niches_policy<'tcx>(tcx: TyCtxt<'tcx>, _: LocalCrate) -> ReferenceN /// crates not specifying `-Z reference-niches`. const DEFAULT_REF_NICHES: ReferenceNichePolicy = ReferenceNichePolicy { size: false, align: false }; -#[instrument(skip(tcx, query), level = "debug")] -fn naive_layout_of<'tcx>( - tcx: TyCtxt<'tcx>, - query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, -) -> Result, &'tcx LayoutError<'tcx>> { - let (param_env, ty) = query.into_parts(); - debug!(?ty); - - let param_env = param_env.with_reveal_all_normalized(tcx); - let unnormalized_ty = ty; - - // FIXME: We might want to have two different versions of `layout_of`: - // One that can be called after typecheck has completed and can use - // `normalize_erasing_regions` here and another one that can be called - // before typecheck has completed and uses `try_normalize_erasing_regions`. - let ty = match tcx.try_normalize_erasing_regions(param_env, ty) { - Ok(t) => t, - Err(normalization_error) => { - return Err(tcx - .arena - .alloc(LayoutError::NormalizationFailure(ty, normalization_error))); - } - }; - - if ty != unnormalized_ty { - // Ensure this layout is also cached for the normalized type. - return tcx.naive_layout_of(param_env.and(ty)); - } - - let cx = LayoutCx { tcx, param_env }; - let layout = naive_layout_of_uncached(&cx, ty)?; - Ok(TyAndNaiveLayout { ty, layout }) -} - #[instrument(skip(tcx, query), level = "debug")] fn layout_of<'tcx>( tcx: TyCtxt<'tcx>, @@ -90,13 +55,9 @@ fn layout_of<'tcx>( let cx = LayoutCx { tcx, param_env }; let layout = layout_of_uncached(&cx, ty)?; - if !naive.is_refined_by(layout) { - bug!("the naive layout isn't refined by the actual layout:\n{:#?}\n{:#?}", naive, layout,); - } - let layout = TyAndLayout { ty, layout }; record_layout_for_printing(&cx, layout); - sanity_check_layout(&cx, &layout); + sanity_check_layout(&cx, &layout, &naive); Ok(layout) } @@ -108,191 +69,6 @@ fn error<'tcx>( cx.tcx.arena.alloc(err) } -fn naive_layout_of_uncached<'tcx>( - cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, - ty: Ty<'tcx>, -) -> Result> { - let tcx = cx.tcx; - let dl = cx.data_layout(); - - let scalar = |value: Primitive| NaiveLayout { - abi: NaiveAbi::Scalar(value), - size: value.size(dl), - align: value.align(dl).abi, - exact: true, - }; - - let univariant = |fields: &mut dyn Iterator>, - repr: &ReprOptions| - -> Result> { - if repr.pack.is_some() && repr.align.is_some() { - cx.tcx.sess.delay_span_bug(DUMMY_SP, "struct cannot be packed and aligned"); - return Err(error(cx, LayoutError::Unknown(ty))); - } - - let linear = repr.inhibit_struct_field_reordering_opt(); - let pack = repr.pack.unwrap_or(Align::MAX); - let mut layout = NaiveLayout::EMPTY; - - for field in fields { - let field = cx.naive_layout_of(field)?.packed(pack); - if linear { - layout = layout.pad_to_align(field.align); - } - layout = layout - .concat(&field, dl) - .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?; - } - - if let Some(align) = repr.align { - layout = layout.align_to(align); - } - - if linear { - layout.abi = layout.abi.as_aggregate(); - } - - Ok(layout.pad_to_align(layout.align)) - }; - - debug_assert!(!ty.has_non_region_infer()); - - Ok(match *ty.kind() { - // Basic scalars - ty::Bool => scalar(Int(I8, false)), - ty::Char => scalar(Int(I32, false)), - ty::Int(ity) => scalar(Int(Integer::from_int_ty(dl, ity), true)), - ty::Uint(ity) => scalar(Int(Integer::from_uint_ty(dl, ity), false)), - ty::Float(fty) => scalar(match fty { - ty::FloatTy::F32 => F32, - ty::FloatTy::F64 => F64, - }), - ty::FnPtr(_) => scalar(Pointer(dl.instruction_address_space)), - - // The never type. - ty::Never => NaiveLayout { abi: NaiveAbi::Uninhabited, ..NaiveLayout::EMPTY }, - - // Potentially-wide pointers. - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { - let data_ptr = scalar(Pointer(AddressSpace::DATA)); - if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? { - // Effectively a (ptr, meta) tuple. - let l = data_ptr - .concat(&scalar(metadata.primitive()), dl) - .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?; - l.pad_to_align(l.align) - } else { - // No metadata, this is a thin pointer. - data_ptr - } - } - - ty::Dynamic(_, _, ty::DynStar) => { - let ptr = scalar(Pointer(AddressSpace::DATA)); - ptr.concat(&ptr, dl).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))? - } - - // Arrays and slices. - ty::Array(element, count) => { - let count = compute_array_count(cx, count) - .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; - let element = cx.naive_layout_of(element)?; - NaiveLayout { - abi: element.abi.as_aggregate(), - size: element - .size - .checked_mul(count, cx) - .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?, - ..*element - } - } - ty::Slice(element) => { - let element = cx.naive_layout_of(element)?; - NaiveLayout { abi: NaiveAbi::Unsized, size: Size::ZERO, ..*element } - } - - ty::FnDef(..) => NaiveLayout::EMPTY, - - // Unsized types. - ty::Str | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => { - NaiveLayout { abi: NaiveAbi::Unsized, ..NaiveLayout::EMPTY } - } - - // FIXME(reference_niches): try to actually compute a reasonable layout estimate, - // without duplicating too much code from `generator_layout`. - ty::Generator(..) => NaiveLayout { exact: false, ..NaiveLayout::EMPTY }, - - ty::Closure(_, ref substs) => { - univariant(&mut substs.as_closure().upvar_tys(), &ReprOptions::default())? - } - - ty::Tuple(tys) => univariant(&mut tys.iter(), &ReprOptions::default())?, - - ty::Adt(def, substs) if def.is_union() => { - let repr = def.repr(); - let pack = repr.pack.unwrap_or(Align::MAX); - if repr.pack.is_some() && repr.align.is_some() { - cx.tcx.sess.delay_span_bug(DUMMY_SP, "union cannot be packed and aligned"); - return Err(error(cx, LayoutError::Unknown(ty))); - } - - let mut layout = NaiveLayout::EMPTY; - for f in &def.variants()[FIRST_VARIANT].fields { - let field = cx.naive_layout_of(f.ty(tcx, substs))?; - layout = layout.union(&field.packed(pack)); - } - - // Unions are always inhabited, and never scalar if `repr(C)`. - if !matches!(layout.abi, NaiveAbi::Scalar(_)) || repr.inhibit_enum_layout_opt() { - layout.abi = NaiveAbi::Sized; - } - - if let Some(align) = repr.align { - layout = layout.align_to(align); - } - layout.pad_to_align(layout.align) - } - - ty::Adt(def, substs) => { - let repr = def.repr(); - let base = NaiveLayout { - // For simplicity, assume that any enum has its discriminant field (if it exists) - // niched inside one of the variants; this will underestimate the size (and sometimes - // alignment) of enums. We also doesn't compute exact alignment for SIMD structs. - // FIXME(reference_niches): Be smarter here. - // Also consider adding a special case for null-optimized enums, so that we can have - // `Option<&T>: PointerLike` in generic contexts. - exact: !def.is_enum() && !repr.simd(), - // An ADT with no inhabited variants should have an uninhabited ABI. - abi: NaiveAbi::Uninhabited, - ..NaiveLayout::EMPTY - }; - - let layout = def.variants().iter().try_fold(base, |layout, v| { - let mut fields = v.fields.iter().map(|f| f.ty(tcx, substs)); - let vlayout = univariant(&mut fields, &repr)?; - Ok(layout.union(&vlayout)) - })?; - layout.pad_to_align(layout.align) - } - - // Types with no meaningful known layout. - ty::Alias(..) => { - // NOTE(eddyb) `layout_of` query should've normalized these away, - // if that was possible, so there's no reason to try again here. - return Err(error(cx, LayoutError::Unknown(ty))); - } - - ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => { - bug!("Layout::compute: unexpected type `{}`", ty) - } - - ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => { - return Err(error(cx, LayoutError::Unknown(ty))); - } - }) -} - fn univariant_uninterned<'tcx>( cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, ty: Ty<'tcx>, @@ -739,7 +515,7 @@ fn layout_of_uncached<'tcx>( }) } -fn compute_array_count<'tcx>( +pub(crate) fn compute_array_count<'tcx>( cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, mut count: ty::Const<'tcx>, ) -> Option { @@ -754,7 +530,7 @@ fn compute_array_count<'tcx>( count.try_eval_target_usize(tcx, param_env) } -fn ptr_metadata_scalar<'tcx>( +pub(crate) fn ptr_metadata_scalar<'tcx>( cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, pointee: Ty<'tcx>, ) -> Result, &'tcx LayoutError<'tcx>> { diff --git a/compiler/rustc_ty_utils/src/layout_naive.rs b/compiler/rustc_ty_utils/src/layout_naive.rs new file mode 100644 index 000000000000..501b27777faf --- /dev/null +++ b/compiler/rustc_ty_utils/src/layout_naive.rs @@ -0,0 +1,240 @@ +use rustc_middle::query::Providers; +use rustc_middle::ty::layout::{ + IntegerExt, LayoutCx, LayoutError, LayoutOf, NaiveAbi, NaiveLayout, TyAndNaiveLayout, +}; +use rustc_middle::ty::{self, ReprOptions, Ty, TyCtxt, TypeVisitableExt}; + +use rustc_span::DUMMY_SP; +use rustc_target::abi::*; + +use crate::layout::{compute_array_count, ptr_metadata_scalar}; + +pub fn provide(providers: &mut Providers) { + *providers = Providers { naive_layout_of, ..*providers }; +} + +#[instrument(skip(tcx, query), level = "debug")] +fn naive_layout_of<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, +) -> Result, &'tcx LayoutError<'tcx>> { + let (param_env, ty) = query.into_parts(); + debug!(?ty); + + let param_env = param_env.with_reveal_all_normalized(tcx); + let unnormalized_ty = ty; + + // FIXME: We might want to have two different versions of `layout_of`: + // One that can be called after typecheck has completed and can use + // `normalize_erasing_regions` here and another one that can be called + // before typecheck has completed and uses `try_normalize_erasing_regions`. + let ty = match tcx.try_normalize_erasing_regions(param_env, ty) { + Ok(t) => t, + Err(normalization_error) => { + return Err(tcx + .arena + .alloc(LayoutError::NormalizationFailure(ty, normalization_error))); + } + }; + + if ty != unnormalized_ty { + // Ensure this layout is also cached for the normalized type. + return tcx.naive_layout_of(param_env.and(ty)); + } + + let cx = LayoutCx { tcx, param_env }; + let layout = naive_layout_of_uncached(&cx, ty)?; + Ok(TyAndNaiveLayout { ty, layout }) +} + +fn error<'tcx>( + cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, + err: LayoutError<'tcx>, +) -> &'tcx LayoutError<'tcx> { + cx.tcx.arena.alloc(err) +} + +fn naive_layout_of_uncached<'tcx>( + cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, + ty: Ty<'tcx>, +) -> Result> { + let tcx = cx.tcx; + let dl = cx.data_layout(); + + let scalar = |value: Primitive| NaiveLayout { + abi: NaiveAbi::Scalar(value), + size: value.size(dl), + align: value.align(dl).abi, + exact: true, + }; + + let univariant = |fields: &mut dyn Iterator>, + repr: &ReprOptions| + -> Result> { + if repr.pack.is_some() && repr.align.is_some() { + cx.tcx.sess.delay_span_bug(DUMMY_SP, "struct cannot be packed and aligned"); + return Err(error(cx, LayoutError::Unknown(ty))); + } + + let linear = repr.inhibit_struct_field_reordering_opt(); + let pack = repr.pack.unwrap_or(Align::MAX); + let mut layout = NaiveLayout::EMPTY; + + for field in fields { + let field = cx.naive_layout_of(field)?.packed(pack); + if linear { + layout = layout.pad_to_align(field.align); + } + layout = layout + .concat(&field, dl) + .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?; + } + + if let Some(align) = repr.align { + layout = layout.align_to(align); + } + + if linear { + layout.abi = layout.abi.as_aggregate(); + } + + Ok(layout.pad_to_align(layout.align)) + }; + + debug_assert!(!ty.has_non_region_infer()); + + Ok(match *ty.kind() { + // Basic scalars + ty::Bool => scalar(Int(I8, false)), + ty::Char => scalar(Int(I32, false)), + ty::Int(ity) => scalar(Int(Integer::from_int_ty(dl, ity), true)), + ty::Uint(ity) => scalar(Int(Integer::from_uint_ty(dl, ity), false)), + ty::Float(fty) => scalar(match fty { + ty::FloatTy::F32 => F32, + ty::FloatTy::F64 => F64, + }), + ty::FnPtr(_) => scalar(Pointer(dl.instruction_address_space)), + + // The never type. + ty::Never => NaiveLayout { abi: NaiveAbi::Uninhabited, ..NaiveLayout::EMPTY }, + + // Potentially-wide pointers. + ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + let data_ptr = scalar(Pointer(AddressSpace::DATA)); + if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? { + // Effectively a (ptr, meta) tuple. + let l = data_ptr + .concat(&scalar(metadata.primitive()), dl) + .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?; + l.pad_to_align(l.align) + } else { + // No metadata, this is a thin pointer. + data_ptr + } + } + + ty::Dynamic(_, _, ty::DynStar) => { + let ptr = scalar(Pointer(AddressSpace::DATA)); + ptr.concat(&ptr, dl).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))? + } + + // Arrays and slices. + ty::Array(element, count) => { + let count = compute_array_count(cx, count) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; + let element = cx.naive_layout_of(element)?; + NaiveLayout { + abi: element.abi.as_aggregate(), + size: element + .size + .checked_mul(count, cx) + .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?, + ..*element + } + } + ty::Slice(element) => { + let element = cx.naive_layout_of(element)?; + NaiveLayout { abi: NaiveAbi::Unsized, size: Size::ZERO, ..*element } + } + + ty::FnDef(..) => NaiveLayout::EMPTY, + + // Unsized types. + ty::Str | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => { + NaiveLayout { abi: NaiveAbi::Unsized, ..NaiveLayout::EMPTY } + } + + // FIXME(reference_niches): try to actually compute a reasonable layout estimate, + // without duplicating too much code from `generator_layout`. + ty::Generator(..) => NaiveLayout { exact: false, ..NaiveLayout::EMPTY }, + + ty::Closure(_, ref substs) => { + univariant(&mut substs.as_closure().upvar_tys(), &ReprOptions::default())? + } + + ty::Tuple(tys) => univariant(&mut tys.iter(), &ReprOptions::default())?, + + ty::Adt(def, substs) if def.is_union() => { + let repr = def.repr(); + let pack = repr.pack.unwrap_or(Align::MAX); + if repr.pack.is_some() && repr.align.is_some() { + cx.tcx.sess.delay_span_bug(DUMMY_SP, "union cannot be packed and aligned"); + return Err(error(cx, LayoutError::Unknown(ty))); + } + + let mut layout = NaiveLayout::EMPTY; + for f in &def.variants()[FIRST_VARIANT].fields { + let field = cx.naive_layout_of(f.ty(tcx, substs))?; + layout = layout.union(&field.packed(pack)); + } + + // Unions are always inhabited, and never scalar if `repr(C)`. + if !matches!(layout.abi, NaiveAbi::Scalar(_)) || repr.inhibit_enum_layout_opt() { + layout.abi = NaiveAbi::Sized; + } + + if let Some(align) = repr.align { + layout = layout.align_to(align); + } + layout.pad_to_align(layout.align) + } + + ty::Adt(def, substs) => { + let repr = def.repr(); + let base = NaiveLayout { + // For simplicity, assume that any enum has its discriminant field (if it exists) + // niched inside one of the variants; this will underestimate the size (and sometimes + // alignment) of enums. We also doesn't compute exact alignment for SIMD structs. + // FIXME(reference_niches): Be smarter here. + // Also consider adding a special case for null-optimized enums, so that we can have + // `Option<&T>: PointerLike` in generic contexts. + exact: !def.is_enum() && !repr.simd(), + // An ADT with no inhabited variants should have an uninhabited ABI. + abi: NaiveAbi::Uninhabited, + ..NaiveLayout::EMPTY + }; + + let layout = def.variants().iter().try_fold(base, |layout, v| { + let mut fields = v.fields.iter().map(|f| f.ty(tcx, substs)); + let vlayout = univariant(&mut fields, &repr)?; + Ok(layout.union(&vlayout)) + })?; + layout.pad_to_align(layout.align) + } + + // Types with no meaningful known layout. + ty::Alias(..) => { + // NOTE(eddyb) `layout_of` query should've normalized these away, + // if that was possible, so there's no reason to try again here. + return Err(error(cx, LayoutError::Unknown(ty))); + } + + ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => { + bug!("Layout::compute: unexpected type `{}`", ty) + } + + ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => { + return Err(error(cx, LayoutError::Unknown(ty))); + } + }) +} diff --git a/compiler/rustc_ty_utils/src/layout_sanity_check.rs b/compiler/rustc_ty_utils/src/layout_sanity_check.rs index 8633334381ad..2e3fe4e7fb84 100644 --- a/compiler/rustc_ty_utils/src/layout_sanity_check.rs +++ b/compiler/rustc_ty_utils/src/layout_sanity_check.rs @@ -1,5 +1,5 @@ use rustc_middle::ty::{ - layout::{LayoutCx, TyAndLayout}, + layout::{LayoutCx, NaiveLayout, TyAndLayout}, TyCtxt, }; use rustc_target::abi::*; @@ -10,6 +10,7 @@ use std::assert_matches::assert_matches; pub(super) fn sanity_check_layout<'tcx>( cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, layout: &TyAndLayout<'tcx>, + naive: &NaiveLayout, ) { // Type-level uninhabitedness should always imply ABI uninhabitedness. if layout.ty.is_privately_uninhabited(cx.tcx, cx.param_env) { @@ -20,6 +21,10 @@ pub(super) fn sanity_check_layout<'tcx>( bug!("size is not a multiple of align, in the following layout:\n{layout:#?}"); } + if !naive.is_refined_by(layout.layout) { + bug!("the naive layout isn't refined by the actual layout:\n{:#?}\n{:#?}", naive, layout); + } + if !cfg!(debug_assertions) { // Stop here, the rest is kind of expensive. return; diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 55b8857ed391..e2db6a6993f9 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -31,6 +31,7 @@ mod errors; mod implied_bounds; pub mod instance; mod layout; +mod layout_naive; mod layout_sanity_check; mod needs_drop; mod opaque_types; @@ -47,6 +48,7 @@ pub fn provide(providers: &mut Providers) { consts::provide(providers); implied_bounds::provide(providers); layout::provide(providers); + layout_naive::provide(providers); needs_drop::provide(providers); opaque_types::provide(providers); representability::provide(providers); From 39cfe70e4fdf9679ce1be55c345dd3f72f53b615 Mon Sep 17 00:00:00 2001 From: Moulins Date: Sat, 1 Jul 2023 23:54:06 +0200 Subject: [PATCH 095/113] CTFE: move `target_{i, u}size_{min, max)` to `rustc_abi::TargetDataLayout` --- compiler/rustc_abi/src/lib.rs | 24 ++++++++------- .../src/const_eval/machine.rs | 7 ++--- .../src/interpret/intrinsics.rs | 11 ++++--- .../rustc_const_eval/src/interpret/memory.rs | 2 +- .../rustc_middle/src/mir/interpret/pointer.rs | 30 +++++-------------- src/tools/miri/src/intptrcast.rs | 2 +- src/tools/miri/src/shims/mod.rs | 4 ++- src/tools/miri/src/shims/unix/fs.rs | 6 ++-- 8 files changed, 37 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 3c64e89e3d25..be1db7311de8 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -354,17 +354,19 @@ impl TargetDataLayout { } } - /// Returns the theoretical maximum address. - /// - /// Note that this doesn't take into account target-specific limitations. #[inline] - pub fn max_address(&self) -> u64 { - match self.pointer_size.bits() { - 16 => u16::MAX.into(), - 32 => u32::MAX.into(), - 64 => u64::MAX, - bits => panic!("max_address: unknown pointer bit size {}", bits), - } + pub fn target_usize_max(&self) -> u64 { + self.pointer_size.unsigned_int_max().try_into().unwrap() + } + + #[inline] + pub fn target_isize_min(&self) -> i64 { + self.pointer_size.signed_int_min().try_into().unwrap() + } + + #[inline] + pub fn target_isize_max(&self) -> i64 { + self.pointer_size.signed_int_max().try_into().unwrap() } /// Returns the (inclusive) range of possible addresses for an allocation with @@ -373,7 +375,7 @@ impl TargetDataLayout { /// Note that this doesn't take into account target-specific limitations. #[inline] pub fn address_range_for(&self, size: Size, align: Align) -> (u64, u64) { - let end = Size::from_bytes(self.max_address()); + let end = Size::from_bytes(self.target_usize_max()); let min = align.bytes(); let max = (end - size).align_down_to(align).bytes(); (min, max) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 51012da6b904..0a9a47b28379 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -1,7 +1,6 @@ use rustc_hir::def::DefKind; use rustc_hir::{LangItem, CRATE_HIR_ID}; use rustc_middle::mir; -use rustc_middle::mir::interpret::PointerArithmetic; use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint::builtin::INVALID_ALIGNMENT; @@ -17,7 +16,7 @@ use rustc_ast::Mutability; use rustc_hir::def_id::DefId; use rustc_middle::mir::AssertMessage; use rustc_span::symbol::{sym, Symbol}; -use rustc_target::abi::{Align, Size}; +use rustc_target::abi::{Align, HasDataLayout as _, Size}; use rustc_target::spec::abi::Abi as CallAbi; use crate::errors::{LongRunning, LongRunningWarn}; @@ -304,8 +303,8 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { Ok(ControlFlow::Break(())) } else { // Not alignable in const, return `usize::MAX`. - let usize_max = Scalar::from_target_usize(self.target_usize_max(), self); - self.write_scalar(usize_max, dest)?; + let usize_max = self.data_layout().target_usize_max(); + self.write_scalar(Scalar::from_target_usize(usize_max, self), dest)?; self.return_to_block(ret)?; Ok(ControlFlow::Break(())) } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 04cae23f852a..8ec9a71bf3a9 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -5,9 +5,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::mir::{ self, - interpret::{ - Allocation, ConstAllocation, ConstValue, GlobalId, InterpResult, PointerArithmetic, Scalar, - }, + interpret::{Allocation, ConstAllocation, ConstValue, GlobalId, InterpResult, Scalar}, BinOp, NonDivergingIntrinsic, }; use rustc_middle::ty; @@ -15,7 +13,7 @@ use rustc_middle::ty::layout::{LayoutOf as _, ValidityRequirement}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; -use rustc_target::abi::{Abi, Align, Primitive, Size}; +use rustc_target::abi::{Abi, Align, HasDataLayout as _, Primitive, Size}; use super::{ util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy, @@ -361,11 +359,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { )?; // Perform division by size to compute return value. + let dl = self.data_layout(); let ret_layout = if intrinsic_name == sym::ptr_offset_from_unsigned { - assert!(0 <= dist && dist <= self.target_isize_max()); + assert!(0 <= dist && dist <= dl.target_isize_max()); usize_layout } else { - assert!(self.target_isize_min() <= dist && dist <= self.target_isize_max()); + assert!(dl.target_isize_min() <= dist && dist <= dl.target_isize_max()); isize_layout }; let pointee_layout = self.layout_of(instance_args.type_at(0))?; diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 10a2a70364b8..29fc5ffcfe7c 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1244,7 +1244,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if offset > size.bytes() { // If the pointer is out-of-bounds, we do not have a // meaningful range to return. - 0..=dl.max_address() + 0..=dl.target_usize_max() } else { let (min, max) = dl.address_range_for(size, align); (min + offset)..=(max + offset) diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index 65d04919357f..c8133bcc387a 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -19,33 +19,19 @@ pub trait PointerArithmetic: HasDataLayout { #[inline(always)] fn max_size_of_val(&self) -> Size { - Size::from_bytes(self.target_isize_max()) - } - - #[inline] - fn target_usize_max(&self) -> u64 { - self.pointer_size().unsigned_int_max().try_into().unwrap() - } - - #[inline] - fn target_isize_min(&self) -> i64 { - self.pointer_size().signed_int_min().try_into().unwrap() - } - - #[inline] - fn target_isize_max(&self) -> i64 { - self.pointer_size().signed_int_max().try_into().unwrap() + Size::from_bytes(self.data_layout().target_isize_max()) } #[inline] fn target_usize_to_isize(&self, val: u64) -> i64 { + let dl = self.data_layout(); let val = val as i64; // Now wrap-around into the machine_isize range. - if val > self.target_isize_max() { + if val > dl.target_isize_max() { // This can only happen if the ptr size is < 64, so we know max_usize_plus_1 fits into // i64. - debug_assert!(self.pointer_size().bits() < 64); - let max_usize_plus_1 = 1u128 << self.pointer_size().bits(); + debug_assert!(dl.pointer_size.bits() < 64); + let max_usize_plus_1 = 1u128 << dl.pointer_size.bits(); val - i64::try_from(max_usize_plus_1).unwrap() } else { val @@ -58,7 +44,7 @@ pub trait PointerArithmetic: HasDataLayout { #[inline] fn truncate_to_ptr(&self, (val, over): (u64, bool)) -> (u64, bool) { let val = u128::from(val); - let max_ptr_plus_1 = 1u128 << self.pointer_size().bits(); + let max_ptr_plus_1 = 1u128 << self.data_layout().pointer_size.bits(); (u64::try_from(val % max_ptr_plus_1).unwrap(), over || val >= max_ptr_plus_1) } @@ -76,11 +62,11 @@ pub trait PointerArithmetic: HasDataLayout { let n = i.unsigned_abs(); if i >= 0 { let (val, over) = self.overflowing_offset(val, n); - (val, over || i > self.target_isize_max()) + (val, over || i > self.data_layout().target_isize_max()) } else { let res = val.overflowing_sub(n); let (val, over) = self.truncate_to_ptr(res); - (val, over || i < self.target_isize_min()) + (val, over || i < self.data_layout().target_isize_min()) } } diff --git a/src/tools/miri/src/intptrcast.rs b/src/tools/miri/src/intptrcast.rs index 4fd0af35304e..a43ac61da74a 100644 --- a/src/tools/miri/src/intptrcast.rs +++ b/src/tools/miri/src/intptrcast.rs @@ -207,7 +207,7 @@ impl<'mir, 'tcx> GlobalStateInner { .checked_add(max(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 > ecx.target_usize_max() { + if global_state.next_base_addr > ecx.data_layout().target_usize_max() { throw_exhaust!(AddressSpaceFull); } // Given that `next_base_addr` increases in each allocation, pushing the diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index 1027b24e3011..0caa9b522f94 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -21,6 +21,7 @@ use log::trace; use rustc_middle::{mir, ty}; use rustc_target::spec::abi::Abi; +use rustc_target::abi::HasDataLayout as _; use crate::*; use helpers::check_arg_count; @@ -108,7 +109,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } // Return error result (usize::MAX), and jump to caller. - this.write_scalar(Scalar::from_target_usize(this.target_usize_max(), this), dest)?; + let usize_max = this.data_layout().target_usize_max(); + this.write_scalar(Scalar::from_target_usize(usize_max, this), dest)?; this.go_to_block(ret); Ok(true) } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 0fdd55b407cd..5da66801694b 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -12,7 +12,7 @@ use log::trace; use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::TyCtxt; -use rustc_target::abi::{Align, Size}; +use rustc_target::abi::{Align, Size, HasDataLayout as _}; use crate::shims::os_str::bytes_to_os_str; use crate::*; @@ -753,7 +753,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // We cap the number of read bytes to the largest value that we are able to fit in both the // host's and target's `isize`. This saves us from having to handle overflows later. let count = count - .min(u64::try_from(this.target_isize_max()).unwrap()) + .min(u64::try_from(this.data_layout().target_isize_max()).unwrap()) .min(u64::try_from(isize::MAX).unwrap()); let communicate = this.machine.communicate(); @@ -807,7 +807,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // We cap the number of written bytes to the largest value that we are able to fit in both the // host's and target's `isize`. This saves us from having to handle overflows later. let count = count - .min(u64::try_from(this.target_isize_max()).unwrap()) + .min(u64::try_from(this.data_layout().target_isize_max()).unwrap()) .min(u64::try_from(isize::MAX).unwrap()); let communicate = this.machine.communicate(); From 93bcc2ef98264de8acb8e9f623a5efadd6bb09d2 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 20 Jul 2023 22:33:37 -0300 Subject: [PATCH 096/113] Implement Stable for ty::GenericArgs --- compiler/rustc_smir/src/rustc_smir/mod.rs | 49 +++++++++++------------ 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 8999d44133dd..b6ef92575d22 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -8,9 +8,7 @@ //! For now, we are developing everything inside `rustc`, thus, we keep this module private. use crate::rustc_internal::{self, opaque}; -use crate::stable_mir::ty::{ - FloatTy, GenericArgKind, GenericArgs, IntTy, Movability, RigidTy, TyKind, UintTy, -}; +use crate::stable_mir::ty::{FloatTy, IntTy, Movability, RigidTy, TyKind, UintTy}; use crate::stable_mir::{self, Context}; use rustc_hir as hir; use rustc_middle::mir; @@ -103,7 +101,7 @@ impl<'tcx> Tables<'tcx> { }, ty::Adt(adt_def, generic_args) => TyKind::RigidTy(RigidTy::Adt( rustc_internal::adt_def(adt_def.did()), - self.generic_args(generic_args), + generic_args.stable(self), )), ty::Foreign(def_id) => { TyKind::RigidTy(RigidTy::Foreign(rustc_internal::foreign_def(*def_id))) @@ -123,17 +121,17 @@ impl<'tcx> Tables<'tcx> { )), ty::FnDef(def_id, generic_args) => TyKind::RigidTy(RigidTy::FnDef( rustc_internal::fn_def(*def_id), - self.generic_args(generic_args), + generic_args.stable(self), )), ty::FnPtr(_) => todo!(), ty::Dynamic(_, _, _) => todo!(), ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure( rustc_internal::closure_def(*def_id), - self.generic_args(generic_args), + generic_args.stable(self), )), ty::Generator(def_id, generic_args, movability) => TyKind::RigidTy(RigidTy::Generator( rustc_internal::generator_def(*def_id), - self.generic_args(generic_args), + generic_args.stable(self), match movability { hir::Movability::Static => Movability::Static, hir::Movability::Movable => Movability::Movable, @@ -164,24 +162,6 @@ impl<'tcx> Tables<'tcx> { self.types.push(ty); stable_mir::ty::Ty(id) } - - fn generic_args( - &mut self, - generic_args: &ty::GenericArgs<'tcx>, - ) -> stable_mir::ty::GenericArgs { - GenericArgs( - generic_args - .iter() - .map(|arg| match arg.unpack() { - ty::GenericArgKind::Lifetime(region) => { - GenericArgKind::Lifetime(opaque(®ion)) - } - ty::GenericArgKind::Type(ty) => GenericArgKind::Type(self.intern_ty(ty)), - ty::GenericArgKind::Const(const_) => GenericArgKind::Const(opaque(&const_)), - }) - .collect(), - ) - } } /// Build a stable mir crate from a given crate number. @@ -582,3 +562,22 @@ impl<'tcx> Stable<'tcx> for mir::Terminator<'tcx> { } } } + +impl<'tcx> Stable<'tcx> for ty::GenericArgs<'tcx> { + type T = stable_mir::ty::GenericArgs; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::{GenericArgKind, GenericArgs}; + + GenericArgs( + self.iter() + .map(|arg| match arg.unpack() { + ty::GenericArgKind::Lifetime(region) => { + GenericArgKind::Lifetime(opaque(®ion)) + } + ty::GenericArgKind::Type(ty) => GenericArgKind::Type(tables.intern_ty(ty)), + ty::GenericArgKind::Const(const_) => GenericArgKind::Const(opaque(&const_)), + }) + .collect(), + ) + } +} From 17b8977f9b3f696c71771224888595367bc96deb Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 20 Jul 2023 23:16:55 -0300 Subject: [PATCH 097/113] Add FnPtr ty to SMIR --- compiler/rustc_smir/src/rustc_internal/mod.rs | 16 +++ compiler/rustc_smir/src/rustc_smir/mod.rs | 97 ++++++++++++++++++- compiler/rustc_smir/src/stable_mir/ty.rs | 79 +++++++++++++++ 3 files changed, 191 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index ccb12c27107e..a918bc981c03 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -47,6 +47,14 @@ pub fn generator_def(did: DefId) -> stable_mir::ty::GeneratorDef { with_tables(|t| t.generator_def(did)) } +pub fn param_def(did: DefId) -> stable_mir::ty::ParamDef { + with_tables(|t| t.param_def(did)) +} + +pub fn br_named_def(did: DefId) -> stable_mir::ty::BrNamedDef { + with_tables(|t| t.br_named_def(did)) +} + impl<'tcx> Tables<'tcx> { pub fn item_def_id(&self, item: &stable_mir::CrateItem) -> DefId { self.def_ids[item.0] @@ -76,6 +84,14 @@ impl<'tcx> Tables<'tcx> { stable_mir::ty::GeneratorDef(self.create_def_id(did)) } + pub fn param_def(&mut self, did: DefId) -> stable_mir::ty::ParamDef { + stable_mir::ty::ParamDef(self.create_def_id(did)) + } + + pub fn br_named_def(&mut self, did: DefId) -> stable_mir::ty::BrNamedDef { + stable_mir::ty::BrNamedDef(self.create_def_id(did)) + } + fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId { // FIXME: this becomes inefficient when we have too many ids for (i, &d) in self.def_ids.iter().enumerate() { diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index b6ef92575d22..ced9a102c9a8 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -123,7 +123,7 @@ impl<'tcx> Tables<'tcx> { rustc_internal::fn_def(*def_id), generic_args.stable(self), )), - ty::FnPtr(_) => todo!(), + ty::FnPtr(poly_fn_sig) => TyKind::RigidTy(RigidTy::FnPtr(poly_fn_sig.stable(self))), ty::Dynamic(_, _, _) => todo!(), ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure( rustc_internal::closure_def(*def_id), @@ -581,3 +581,98 @@ impl<'tcx> Stable<'tcx> for ty::GenericArgs<'tcx> { ) } } + +impl<'tcx> Stable<'tcx> for ty::PolyFnSig<'tcx> { + type T = stable_mir::ty::PolyFnSig; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::Binder; + + Binder { + value: self.skip_binder().stable(tables), + bound_vars: self + .bound_vars() + .iter() + .map(|bound_var| bound_var.stable(tables)) + .collect(), + } + } +} + +impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> { + type T = stable_mir::ty::FnSig; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use rustc_target::spec::abi; + use stable_mir::ty::{Abi, FnSig, Unsafety}; + + FnSig { + inputs_and_output: self + .inputs_and_output + .iter() + .map(|ty| tables.intern_ty(ty)) + .collect(), + c_variadic: self.c_variadic, + unsafety: match self.unsafety { + hir::Unsafety::Normal => Unsafety::Normal, + hir::Unsafety::Unsafe => Unsafety::Unsafe, + }, + abi: match self.abi { + abi::Abi::Rust => Abi::Rust, + abi::Abi::C { unwind } => Abi::C { unwind }, + abi::Abi::Cdecl { unwind } => Abi::Cdecl { unwind }, + abi::Abi::Stdcall { unwind } => Abi::Stdcall { unwind }, + abi::Abi::Fastcall { unwind } => Abi::Fastcall { unwind }, + abi::Abi::Vectorcall { unwind } => Abi::Vectorcall { unwind }, + abi::Abi::Thiscall { unwind } => Abi::Thiscall { unwind }, + abi::Abi::Aapcs { unwind } => Abi::Aapcs { unwind }, + abi::Abi::Win64 { unwind } => Abi::Win64 { unwind }, + abi::Abi::SysV64 { unwind } => Abi::SysV64 { unwind }, + abi::Abi::PtxKernel => Abi::PtxKernel, + abi::Abi::Msp430Interrupt => Abi::Msp430Interrupt, + abi::Abi::X86Interrupt => Abi::X86Interrupt, + abi::Abi::AmdGpuKernel => Abi::AmdGpuKernel, + abi::Abi::EfiApi => Abi::EfiApi, + abi::Abi::AvrInterrupt => Abi::AvrInterrupt, + abi::Abi::AvrNonBlockingInterrupt => Abi::AvrNonBlockingInterrupt, + abi::Abi::CCmseNonSecureCall => Abi::CCmseNonSecureCall, + abi::Abi::Wasm => Abi::Wasm, + abi::Abi::System { unwind } => Abi::System { unwind }, + abi::Abi::RustIntrinsic => Abi::RustIntrinsic, + abi::Abi::RustCall => Abi::RustCall, + abi::Abi::PlatformIntrinsic => Abi::PlatformIntrinsic, + abi::Abi::Unadjusted => Abi::Unadjusted, + abi::Abi::RustCold => Abi::RustCold, + }, + } + } +} + +impl<'tcx> Stable<'tcx> for ty::BoundVariableKind { + type T = stable_mir::ty::BoundVariableKind; + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::{BoundRegionKind, BoundTyKind, BoundVariableKind}; + + match self { + ty::BoundVariableKind::Ty(bound_ty_kind) => { + BoundVariableKind::Ty(match bound_ty_kind { + ty::BoundTyKind::Anon => BoundTyKind::Anon, + ty::BoundTyKind::Param(def_id, symbol) => { + BoundTyKind::Param(rustc_internal::param_def(*def_id), symbol.to_string()) + } + }) + } + ty::BoundVariableKind::Region(bound_region_kind) => { + BoundVariableKind::Region(match bound_region_kind { + ty::BoundRegionKind::BrAnon(option_span) => { + BoundRegionKind::BrAnon(option_span.map(|span| opaque(&span))) + } + ty::BoundRegionKind::BrNamed(def_id, symbol) => BoundRegionKind::BrNamed( + rustc_internal::br_named_def(*def_id), + symbol.to_string(), + ), + ty::BoundRegionKind::BrEnv => BoundRegionKind::BrEnv, + }) + } + ty::BoundVariableKind::Const => BoundVariableKind::Const, + } + } +} diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index ba120be04b2f..2b762eab5ef2 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -12,6 +12,7 @@ impl Ty { type Const = Opaque; pub(crate) type Region = Opaque; +type Span = Opaque; #[derive(Clone, Debug)] pub enum TyKind { @@ -33,6 +34,7 @@ pub enum RigidTy { RawPtr(Ty, Mutability), Ref(Region, Ty, Mutability), FnDef(FnDef, GenericArgs), + FnPtr(PolyFnSig), Closure(ClosureDef, GenericArgs), Generator(GeneratorDef, GenericArgs, Movability), Never, @@ -83,6 +85,12 @@ pub struct ClosureDef(pub(crate) DefId); #[derive(Clone, PartialEq, Eq, Debug)] pub struct GeneratorDef(pub(crate) DefId); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct ParamDef(pub(crate) DefId); + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct BrNamedDef(pub(crate) DefId); + #[derive(Clone, PartialEq, Eq, Debug)] pub struct AdtDef(pub(crate) DefId); @@ -95,3 +103,74 @@ pub enum GenericArgKind { Type(Ty), Const(Const), } + +pub type PolyFnSig = Binder; + +#[derive(Clone, Debug)] +pub struct FnSig { + pub inputs_and_output: Vec, + pub c_variadic: bool, + pub unsafety: Unsafety, + pub abi: Abi, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum Unsafety { + Unsafe, + Normal, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum Abi { + Rust, + C { unwind: bool }, + Cdecl { unwind: bool }, + Stdcall { unwind: bool }, + Fastcall { unwind: bool }, + Vectorcall { unwind: bool }, + Thiscall { unwind: bool }, + Aapcs { unwind: bool }, + Win64 { unwind: bool }, + SysV64 { unwind: bool }, + PtxKernel, + Msp430Interrupt, + X86Interrupt, + AmdGpuKernel, + EfiApi, + AvrInterrupt, + AvrNonBlockingInterrupt, + CCmseNonSecureCall, + Wasm, + System { unwind: bool }, + RustIntrinsic, + RustCall, + PlatformIntrinsic, + Unadjusted, + RustCold, +} + +#[derive(Clone, Debug)] +pub struct Binder { + pub value: T, + pub bound_vars: Vec, +} + +#[derive(Clone, Debug)] +pub enum BoundVariableKind { + Ty(BoundTyKind), + Region(BoundRegionKind), + Const, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum BoundTyKind { + Anon, + Param(ParamDef, String), +} + +#[derive(Clone, Debug)] +pub enum BoundRegionKind { + BrAnon(Option), + BrNamed(BrNamedDef, String), + BrEnv, +} From 303af36be7c1c9306ea00db7c53308ebfb04d4e4 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 20 Jul 2023 13:21:04 +0200 Subject: [PATCH 098/113] new solver: add a separate cache for coherence --- compiler/rustc_middle/src/ty/context.rs | 12 ++++++--- .../src/solve/search_graph/mod.rs | 25 +++++++++---------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 1a23fa802100..aa1f8e48b1b7 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -569,6 +569,7 @@ pub struct GlobalCtxt<'tcx> { /// Caches the results of goal evaluation in the new solver. pub new_solver_evaluation_cache: solve::EvaluationCache<'tcx>, + pub new_solver_coherence_evaluation_cache: solve::EvaluationCache<'tcx>, /// Data layout specification for the current target. pub data_layout: TargetDataLayout, @@ -680,10 +681,12 @@ impl<'tcx> TyCtxt<'tcx> { value.lift_to_tcx(self) } - /// Creates a type context and call the closure with a `TyCtxt` reference - /// to the context. The closure enforces that the type context and any interned - /// value (types, args, etc.) can only be used while `ty::tls` has a valid - /// reference to the context, to allow formatting values that need it. + /// Creates a type context. To use the context call `fn enter` which + /// provides a `TyCtxt`. + /// + /// By only providing the `TyCtxt` inside of the closure we enforce that the type + /// context and any interned alue (types, args, etc.) can only be used while `ty::tls` + /// has a valid reference to the context, to allow formatting values that need it. pub fn create_global_ctxt( s: &'tcx Session, lint_store: Lrc, @@ -721,6 +724,7 @@ impl<'tcx> TyCtxt<'tcx> { selection_cache: Default::default(), evaluation_cache: Default::default(), new_solver_evaluation_cache: Default::default(), + new_solver_coherence_evaluation_cache: Default::default(), data_layout, alloc_map: Lock::new(interpret::AllocMap::new()), } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index f00456e26df5..98c8a74752c0 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -9,7 +9,9 @@ use cache::ProvisionalCache; use overflow::OverflowData; use rustc_index::IndexVec; use rustc_middle::dep_graph::DepKind; -use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryResult}; +use rustc_middle::traits::solve::{ + CanonicalInput, Certainty, EvaluationCache, MaybeCause, QueryResult, +}; use rustc_middle::ty::TyCtxt; use std::{collections::hash_map::Entry, mem}; @@ -58,10 +60,10 @@ impl<'tcx> SearchGraph<'tcx> { /// /// We could add another global cache for coherence instead, /// but that's effort so let's only do it if necessary. - pub(super) fn should_use_global_cache(&self) -> bool { + pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> { match self.mode { - SolverMode::Normal => true, - SolverMode::Coherence => false, + SolverMode::Normal => &tcx.new_solver_evaluation_cache, + SolverMode::Coherence => &tcx.new_solver_coherence_evaluation_cache, } } @@ -213,8 +215,8 @@ impl<'tcx> SearchGraph<'tcx> { inspect: &mut ProofTreeBuilder<'tcx>, mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx> { - if self.should_use_global_cache() && inspect.use_global_cache() { - if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) { + if inspect.use_global_cache() { + if let Some(result) = self.global_cache(tcx).get(&canonical_input, tcx) { debug!(?canonical_input, ?result, "cache hit"); inspect.cache_hit(CacheHit::Global); return result; @@ -278,13 +280,10 @@ impl<'tcx> SearchGraph<'tcx> { // dependencies, our non-root goal may no longer appear as child of the root goal. // // See https://github.com/rust-lang/rust/pull/108071 for some additional context. - let can_cache = !self.overflow_data.did_overflow() || self.stack.is_empty(); - if self.should_use_global_cache() && can_cache { - tcx.new_solver_evaluation_cache.insert( - current_goal.input, - dep_node, - current_goal.response, - ); + let can_cache = inspect.use_global_cache() + && (!self.overflow_data.did_overflow() || self.stack.is_empty()); + if can_cache { + self.global_cache(tcx).insert(current_goal.input, dep_node, current_goal.response) } } From 3e0389561b8e6145d42c9043e94ae61bc960fa2e Mon Sep 17 00:00:00 2001 From: David Rheinsberg Date: Fri, 21 Jul 2023 10:31:01 +0200 Subject: [PATCH 099/113] rustc_target: drop duplicate code Drop duplicate helper methods on `Layout`, which are already implemented on `LayoutS`. Note that `Layout` has a `Deref` implementation to `LayoutS`, so all accessors are automatically redirected. The methods are identical and have been copied to `rustc_abi` in: commit 390a637e296ccfaac4c6abd1291b0523e8a8e00b Author: hamidreza kalbasi Date: Mon Nov 7 00:36:11 2022 +0330 move things from rustc_target::abi to rustc_abi This commit left behind the original implementation. Drop it now. Signed-off-by: David Rheinsberg --- compiler/rustc_target/src/abi/mod.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 589cd3cf96b3..084c917cc317 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -140,24 +140,3 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { offset } } - -impl<'a, Ty> TyAndLayout<'a, Ty> { - /// Returns `true` if the layout corresponds to an unsized type. - pub fn is_unsized(&self) -> bool { - self.abi.is_unsized() - } - - #[inline] - pub fn is_sized(&self) -> bool { - self.abi.is_sized() - } - - /// Returns `true` if the type is a ZST and not unsized. - pub fn is_zst(&self) -> bool { - match self.abi { - Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. } => false, - Abi::Uninhabited => self.size.bytes() == 0, - Abi::Aggregate { sized } => sized && self.size.bytes() == 0, - } - } -} From b0dadff6de0a0d17b38ebfcae333cc88a8b6e47e Mon Sep 17 00:00:00 2001 From: David Rheinsberg Date: Fri, 21 Jul 2023 09:32:28 +0200 Subject: [PATCH 100/113] error/E0691: include alignment in error message Include the computed alignment of the violating field when rejecting transparent types with non-trivially aligned ZSTs. ZST member fields in transparent types must have an alignment of 1 (to ensure it does not raise the layout requirements of the transparent field). The current error message looks like this: LL | struct Foobar(u32, [u32; 0]); | ^^^^^^^^ has alignment larger than 1 This patch changes the report to include the alignment of the violating field: LL | struct Foobar(u32, [u32; 0]); | ^^^^^^^^ has alignment of 4, which is larger than 1 In case of unknown alignments, it will yield: LL | struct Foobar(u32, [T; 0]); | ^^^^^^ may have alignment larger than 1 This allows developers to get a better grasp why a specific field is rejected. Knowing the alignment of the violating field makes it easier to judge where that alignment-requirement originates, and thus hopefully provide better hints on how to mitigate the problem. This idea was proposed in 2022 in #98071 as part of a bigger change. This commit simply extracts this error-message change, to decouple it from the other diagnostic improvements. --- .../src/error_codes/E0691.md | 3 +- .../rustc_hir_analysis/src/check/check.rs | 29 ++++++++++++------- tests/ui/repr/repr-transparent.stderr | 8 ++--- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0691.md b/compiler/rustc_error_codes/src/error_codes/E0691.md index 60060cacbd60..483c74c0ff56 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0691.md +++ b/compiler/rustc_error_codes/src/error_codes/E0691.md @@ -11,7 +11,8 @@ struct ForceAlign32; #[repr(transparent)] struct Wrapper(f32, ForceAlign32); // error: zero-sized field in transparent - // struct has alignment larger than 1 + // struct has alignment of 32, which + // is larger than 1 ``` A transparent struct, enum, or union is supposed to be represented exactly like diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index d9e14096954b..e0698a72335f 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1078,9 +1078,9 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) // We are currently checking the type this field came from, so it must be local let span = tcx.hir().span_if_local(field.did).unwrap(); let zst = layout.is_ok_and(|layout| layout.is_zst()); - let align1 = layout.is_ok_and(|layout| layout.align.abi.bytes() == 1); + let align = layout.ok().map(|layout| layout.align.abi.bytes()); if !zst { - return (span, zst, align1, None); + return (span, zst, align, None); } fn check_non_exhaustive<'tcx>( @@ -1115,12 +1115,12 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) } } - (span, zst, align1, check_non_exhaustive(tcx, ty).break_value()) + (span, zst, align, check_non_exhaustive(tcx, ty).break_value()) }); let non_zst_fields = field_infos .clone() - .filter_map(|(span, zst, _align1, _non_exhaustive)| if !zst { Some(span) } else { None }); + .filter_map(|(span, zst, _align, _non_exhaustive)| if !zst { Some(span) } else { None }); let non_zst_count = non_zst_fields.clone().count(); if non_zst_count >= 2 { bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, tcx.def_span(adt.did())); @@ -1128,17 +1128,26 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) let incompatible_zst_fields = field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count(); let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2; - for (span, zst, align1, non_exhaustive) in field_infos { - if zst && !align1 { - struct_span_err!( + for (span, zst, align, non_exhaustive) in field_infos { + if zst && align != Some(1) { + let mut err = struct_span_err!( tcx.sess, span, E0691, "zero-sized field in transparent {} has alignment larger than 1", adt.descr(), - ) - .span_label(span, "has alignment larger than 1") - .emit(); + ); + + if let Some(align_bytes) = align { + err.span_label( + span, + format!("has alignment of {align_bytes}, which is larger than 1"), + ); + } else { + err.span_label(span, "may have alignment larger than 1"); + } + + err.emit(); } if incompat && let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive { tcx.struct_span_lint_hir( diff --git a/tests/ui/repr/repr-transparent.stderr b/tests/ui/repr/repr-transparent.stderr index cb1e23377765..028fc25db46c 100644 --- a/tests/ui/repr/repr-transparent.stderr +++ b/tests/ui/repr/repr-transparent.stderr @@ -20,13 +20,13 @@ error[E0691]: zero-sized field in transparent struct has alignment larger than 1 --> $DIR/repr-transparent.rs:36:32 | LL | struct NontrivialAlignZst(u32, [u16; 0]); - | ^^^^^^^^ has alignment larger than 1 + | ^^^^^^^^ has alignment of 2, which is larger than 1 error[E0691]: zero-sized field in transparent struct has alignment larger than 1 --> $DIR/repr-transparent.rs:42:24 | LL | struct GenericAlign(ZstAlign32, u32); - | ^^^^^^^^^^^^^ has alignment larger than 1 + | ^^^^^^^^^^^^^ has alignment of 32, which is larger than 1 error[E0084]: unsupported representation for zero-variant enum --> $DIR/repr-transparent.rs:44:1 @@ -66,13 +66,13 @@ error[E0691]: zero-sized field in transparent enum has alignment larger than 1 --> $DIR/repr-transparent.rs:71:14 | LL | Foo(u32, [u16; 0]), - | ^^^^^^^^ has alignment larger than 1 + | ^^^^^^^^ has alignment of 2, which is larger than 1 error[E0691]: zero-sized field in transparent enum has alignment larger than 1 --> $DIR/repr-transparent.rs:76:11 | LL | Foo { bar: ZstAlign32, baz: u32 } - | ^^^^^^^^^^^^^^^^^^ has alignment larger than 1 + | ^^^^^^^^^^^^^^^^^^ has alignment of 32, which is larger than 1 error[E0690]: transparent union needs at most one non-zero-sized field, but has 2 --> $DIR/repr-transparent.rs:85:1 From 9346519b0502ad8fd3cff7d77362582a00d215c1 Mon Sep 17 00:00:00 2001 From: cherryblossom000 <31467609+cherryblossom000@users.noreply.github.com> Date: Fri, 16 Apr 2021 20:28:15 +1000 Subject: [PATCH 101/113] Add missing code fence --- src/doc/style-guide/src/expressions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index 0aed8763a062..de8eba1dc6e7 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -178,10 +178,12 @@ let f = Foo { Functional record update syntax is treated like a field, but it must never have a trailing comma. Do not put a space after `..`. +```rust let f = Foo { field1, ..an_expr }; +``` ### Tuple literals From 7577e78ce0bb8f0f91c14de8a793b06e3f7a39b0 Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Wed, 27 May 2020 22:20:26 +0800 Subject: [PATCH 102/113] Use roman 4 letter instead of word Long text without numeric numbers when numeric numbers are used are hard to read. --- src/doc/style-guide/src/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md index 29204603e58d..6bdb3ae26ace 100644 --- a/src/doc/style-guide/src/README.md +++ b/src/doc/style-guide/src/README.md @@ -37,8 +37,8 @@ options. ### Indentation and line width * Use spaces, not tabs. -* Each level of indentation must be four spaces (that is, all indentation - outside of string literals and comments must be a multiple of four). +* Each level of indentation must be 4 spaces (that is, all indentation + outside of string literals and comments must be a multiple of 4). * The maximum width for a line is 100 characters. #### Block indent From 2fc1de3c96bcd6a8c7031109c8b4ce134871d329 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 28 Aug 2020 16:42:47 -0700 Subject: [PATCH 103/113] Clarify conditions for single-line blocks Use consistent phrasing, and add an "and". --- src/doc/style-guide/src/expressions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index de8eba1dc6e7..3068825e0f92 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -64,8 +64,8 @@ Write an empty block as `{}`. Write a block on a single line if: * it is either used in expression position (not statement position) or is an - unsafe block in statement position -* it contains a single-line expression and no statements + unsafe block in statement position, +* it contains a single-line expression and no statements, and * it contains no comments For a single-line block, put spaces after the opening brace and before the From 9539af86c31b05a83cde43bb888a2232decf73d4 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 28 Aug 2020 16:44:34 -0700 Subject: [PATCH 104/113] Clarify guide for unbraced closures: grammatical consistency --- src/doc/style-guide/src/expressions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index 3068825e0f92..1870b194d9d8 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -125,9 +125,9 @@ expression of the closure. Between the `|`s, use function definition syntax, but elide types where possible. Use closures without the enclosing `{}`, if possible. Add the `{}` when you have -a return type, when there are statements, there are comments in the body, or the -body expression spans multiple lines and is a control-flow expression. If using -braces, follow the rules above for blocks. Examples: +a return type, when there are statements, when there are comments in the body, +or when the body expression spans multiple lines and is a control-flow +expression. If using braces, follow the rules above for blocks. Examples: ```rust |arg1, arg2| expr From 9362ae1616e98b08a88dcb806a11a366ed1c6b8b Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 28 Aug 2020 16:45:25 -0700 Subject: [PATCH 105/113] Clarify guide for unbraced closures, regarding comments --- src/doc/style-guide/src/expressions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index 1870b194d9d8..0d4b97895cdd 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -125,8 +125,8 @@ expression of the closure. Between the `|`s, use function definition syntax, but elide types where possible. Use closures without the enclosing `{}`, if possible. Add the `{}` when you have -a return type, when there are statements, when there are comments in the body, -or when the body expression spans multiple lines and is a control-flow +a return type, when there are statements, when there are comments inside the +closure, or when the body expression spans multiple lines and is a control-flow expression. If using braces, follow the rules above for blocks. Examples: ```rust From 9ea1180cc7e1ec93d3398c8aefa32dcbff21e989 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 28 Aug 2020 16:46:07 -0700 Subject: [PATCH 106/113] Simplify wording in guide for unbraced closures --- src/doc/style-guide/src/expressions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index 0d4b97895cdd..bf3fe87a0a46 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -126,8 +126,8 @@ but elide types where possible. Use closures without the enclosing `{}`, if possible. Add the `{}` when you have a return type, when there are statements, when there are comments inside the -closure, or when the body expression spans multiple lines and is a control-flow -expression. If using braces, follow the rules above for blocks. Examples: +closure, or when the body expression is a control-flow expression that spans +multiple lines. If using braces, follow the rules above for blocks. Examples: ```rust |arg1, arg2| expr From 7f109086ee9458eb39f920fb04e4f37a97853701 Mon Sep 17 00:00:00 2001 From: Moulins Date: Fri, 21 Jul 2023 03:26:14 +0200 Subject: [PATCH 107/113] Track (partial) niche information in `NaiveLayout` Still more complexity, but this allows computing exact `NaiveLayout`s for null-optimized enums, and thus allows calls like `transmute::, &U>()` to work in generic contexts. --- compiler/rustc_abi/src/lib.rs | 10 +- compiler/rustc_middle/src/ty/layout.rs | 58 ++++++-- compiler/rustc_ty_utils/src/layout_naive.rs | 148 ++++++++++++++----- tests/ui/layout/valid_range_oob.stderr | 4 +- tests/ui/transmute/transmute-fat-pointers.rs | 12 ++ 5 files changed, 185 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index be1db7311de8..d396f18d59c2 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1062,9 +1062,15 @@ impl WrappingRange { /// Returns `true` if `size` completely fills the range. #[inline] pub fn is_full_for(&self, size: Size) -> bool { + debug_assert!(self.is_in_range_for(size)); + self.start == (self.end.wrapping_add(1) & size.unsigned_int_max()) + } + + /// Returns `true` if the range is valid for `size`. + #[inline(always)] + pub fn is_in_range_for(&self, size: Size) -> bool { let max_value = size.unsigned_int_max(); - debug_assert!(self.start <= max_value && self.end <= max_value); - self.start == (self.end.wrapping_add(1) & max_value) + self.start <= max_value && self.end <= max_value } } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 34a345aba047..26137e86fa0d 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -655,6 +655,8 @@ impl std::ops::DerefMut for TyAndNaiveLayout<'_> { #[derive(Copy, Clone, Debug, HashStable)] pub struct NaiveLayout { pub abi: NaiveAbi, + /// Niche information, required for tracking non-null enum optimizations. + pub niches: NaiveNiches, /// An underestimate of the layout's size. pub size: Size, /// An underestimate of the layout's required alignment. @@ -663,13 +665,20 @@ pub struct NaiveLayout { pub exact: bool, } +#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)] +pub enum NaiveNiches { + None, + Some, + Maybe, +} + #[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)] pub enum NaiveAbi { - /// A scalar layout, always implies `exact`. + /// A scalar layout, always implies `exact` and a non-zero `size`. Scalar(Primitive), - /// An uninhabited layout. (needed to properly track `Scalar`) + /// An uninhabited layout. (needed to properly track `Scalar` and niches) Uninhabited, - /// An unsized aggregate. (needed to properly track `Scalar`) + /// An unsized aggregate. (needed to properly track `Scalar` and niches) Unsized, /// Any other sized layout. Sized, @@ -687,8 +696,13 @@ impl NaiveAbi { impl NaiveLayout { /// The layout of an empty aggregate, e.g. `()`. - pub const EMPTY: Self = - Self { size: Size::ZERO, align: Align::ONE, exact: true, abi: NaiveAbi::Sized }; + pub const EMPTY: Self = Self { + size: Size::ZERO, + align: Align::ONE, + exact: true, + abi: NaiveAbi::Sized, + niches: NaiveNiches::None, + }; /// Returns whether `self` is a valid approximation of the given full `layout`. /// @@ -699,12 +713,20 @@ impl NaiveLayout { } if let NaiveAbi::Scalar(prim) = self.abi { - assert!(self.exact); - if !matches!(layout.abi(), Abi::Scalar(s) if s.primitive() == prim) { + if !self.exact + || self.size == Size::ZERO + || !matches!(layout.abi(), Abi::Scalar(s) if s.primitive() == prim) + { return false; } } + match (self.niches, layout.largest_niche()) { + (NaiveNiches::None, Some(_)) => return false, + (NaiveNiches::Some, None) => return false, + _ => (), + } + !self.exact || (self.size, self.align) == (layout.size(), layout.align().abi) } @@ -745,6 +767,15 @@ impl NaiveLayout { self } + /// Artificially makes this layout inexact. + #[must_use] + #[inline] + pub fn inexact(mut self) -> Self { + self.abi = self.abi.as_aggregate(); + self.exact = false; + self + } + /// Pads this layout so that its size is a multiple of `align`. #[must_use] #[inline] @@ -777,11 +808,18 @@ impl NaiveLayout { // Default case. (_, _) => Sized, }; - Some(Self { abi, size, align, exact }) + let niches = match (self.niches, other.niches) { + (NaiveNiches::Some, _) | (_, NaiveNiches::Some) => NaiveNiches::Some, + (NaiveNiches::None, NaiveNiches::None) => NaiveNiches::None, + (_, _) => NaiveNiches::Maybe, + }; + Some(Self { abi, size, align, exact, niches }) } /// Returns the layout of `self` superposed with `other`, as in an `enum` /// or an `union`. + /// + /// Note: This always ignore niche information from `other`. #[must_use] #[inline] pub fn union(&self, other: &Self) -> Self { @@ -793,7 +831,7 @@ impl NaiveLayout { let abi = match (self.abi, other.abi) { // The unsized ABI overrides everything. (Unsized, _) | (_, Unsized) => Unsized, - // A scalar union must have a single non ZST-field. + // A scalar union must have a single non ZST-field... (_, s @ Scalar(_)) if exact && self.size == Size::ZERO => s, (s @ Scalar(_), _) if exact && other.size == Size::ZERO => s, // ...or identical scalar fields. @@ -802,7 +840,7 @@ impl NaiveLayout { (Uninhabited, Uninhabited) => Uninhabited, (_, _) => Sized, }; - Self { abi, size, align, exact } + Self { abi, size, align, exact, niches: self.niches } } } diff --git a/compiler/rustc_ty_utils/src/layout_naive.rs b/compiler/rustc_ty_utils/src/layout_naive.rs index 501b27777faf..3070ab59d531 100644 --- a/compiler/rustc_ty_utils/src/layout_naive.rs +++ b/compiler/rustc_ty_utils/src/layout_naive.rs @@ -1,12 +1,14 @@ use rustc_middle::query::Providers; use rustc_middle::ty::layout::{ - IntegerExt, LayoutCx, LayoutError, LayoutOf, NaiveAbi, NaiveLayout, TyAndNaiveLayout, + IntegerExt, LayoutCx, LayoutError, LayoutOf, NaiveAbi, NaiveLayout, NaiveNiches, + TyAndNaiveLayout, }; use rustc_middle::ty::{self, ReprOptions, Ty, TyCtxt, TypeVisitableExt}; - use rustc_span::DUMMY_SP; use rustc_target::abi::*; +use std::ops::Bound; + use crate::layout::{compute_array_count, ptr_metadata_scalar}; pub fn provide(providers: &mut Providers) { @@ -61,8 +63,9 @@ fn naive_layout_of_uncached<'tcx>( let tcx = cx.tcx; let dl = cx.data_layout(); - let scalar = |value: Primitive| NaiveLayout { + let scalar = |niched: bool, value: Primitive| NaiveLayout { abi: NaiveAbi::Scalar(value), + niches: if niched { NaiveNiches::Some } else { NaiveNiches::None }, size: value.size(dl), align: value.align(dl).abi, exact: true, @@ -105,26 +108,30 @@ fn naive_layout_of_uncached<'tcx>( Ok(match *ty.kind() { // Basic scalars - ty::Bool => scalar(Int(I8, false)), - ty::Char => scalar(Int(I32, false)), - ty::Int(ity) => scalar(Int(Integer::from_int_ty(dl, ity), true)), - ty::Uint(ity) => scalar(Int(Integer::from_uint_ty(dl, ity), false)), - ty::Float(fty) => scalar(match fty { - ty::FloatTy::F32 => F32, - ty::FloatTy::F64 => F64, - }), - ty::FnPtr(_) => scalar(Pointer(dl.instruction_address_space)), + ty::Bool => scalar(true, Int(I8, false)), + ty::Char => scalar(true, Int(I32, false)), + ty::Int(ity) => scalar(false, Int(Integer::from_int_ty(dl, ity), true)), + ty::Uint(ity) => scalar(false, Int(Integer::from_uint_ty(dl, ity), false)), + ty::Float(fty) => scalar( + false, + match fty { + ty::FloatTy::F32 => F32, + ty::FloatTy::F64 => F64, + }, + ), + ty::FnPtr(_) => scalar(true, Pointer(dl.instruction_address_space)), // The never type. ty::Never => NaiveLayout { abi: NaiveAbi::Uninhabited, ..NaiveLayout::EMPTY }, // Potentially-wide pointers. ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { - let data_ptr = scalar(Pointer(AddressSpace::DATA)); + let data_ptr = scalar(!ty.is_unsafe_ptr(), Pointer(AddressSpace::DATA)); if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? { // Effectively a (ptr, meta) tuple. + let meta = scalar(!metadata.is_always_valid(dl), metadata.primitive()); let l = data_ptr - .concat(&scalar(metadata.primitive()), dl) + .concat(&meta, dl) .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?; l.pad_to_align(l.align) } else { @@ -134,8 +141,9 @@ fn naive_layout_of_uncached<'tcx>( } ty::Dynamic(_, _, ty::DynStar) => { - let ptr = scalar(Pointer(AddressSpace::DATA)); - ptr.concat(&ptr, dl).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))? + let ptr = scalar(false, Pointer(AddressSpace::DATA)); + let vtable = scalar(true, Pointer(AddressSpace::DATA)); + ptr.concat(&vtable, dl).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))? } // Arrays and slices. @@ -149,13 +157,16 @@ fn naive_layout_of_uncached<'tcx>( .size .checked_mul(count, cx) .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?, + niches: if count == 0 { NaiveNiches::None } else { element.niches }, ..*element } } - ty::Slice(element) => { - let element = cx.naive_layout_of(element)?; - NaiveLayout { abi: NaiveAbi::Unsized, size: Size::ZERO, ..*element } - } + ty::Slice(element) => NaiveLayout { + abi: NaiveAbi::Unsized, + size: Size::ZERO, + niches: NaiveNiches::None, + ..*cx.naive_layout_of(element)? + }, ty::FnDef(..) => NaiveLayout::EMPTY, @@ -166,7 +177,9 @@ fn naive_layout_of_uncached<'tcx>( // FIXME(reference_niches): try to actually compute a reasonable layout estimate, // without duplicating too much code from `generator_layout`. - ty::Generator(..) => NaiveLayout { exact: false, ..NaiveLayout::EMPTY }, + ty::Generator(..) => { + NaiveLayout { exact: false, niches: NaiveNiches::Maybe, ..NaiveLayout::EMPTY } + } ty::Closure(_, ref substs) => { univariant(&mut substs.as_closure().upvar_tys(), &ReprOptions::default())? @@ -175,6 +188,7 @@ fn naive_layout_of_uncached<'tcx>( ty::Tuple(tys) => univariant(&mut tys.iter(), &ReprOptions::default())?, ty::Adt(def, substs) if def.is_union() => { + assert_eq!(def.variants().len(), 1, "union should have a single variant"); let repr = def.repr(); let pack = repr.pack.unwrap_or(Align::MAX); if repr.pack.is_some() && repr.align.is_some() { @@ -182,7 +196,12 @@ fn naive_layout_of_uncached<'tcx>( return Err(error(cx, LayoutError::Unknown(ty))); } - let mut layout = NaiveLayout::EMPTY; + let mut layout = NaiveLayout { + // Unions never have niches. + niches: NaiveNiches::None, + ..NaiveLayout::EMPTY + }; + for f in &def.variants()[FIRST_VARIANT].fields { let field = cx.naive_layout_of(f.ty(tcx, substs))?; layout = layout.union(&field.packed(pack)); @@ -201,24 +220,87 @@ fn naive_layout_of_uncached<'tcx>( ty::Adt(def, substs) => { let repr = def.repr(); - let base = NaiveLayout { - // For simplicity, assume that any enum has its discriminant field (if it exists) - // niched inside one of the variants; this will underestimate the size (and sometimes - // alignment) of enums. We also doesn't compute exact alignment for SIMD structs. - // FIXME(reference_niches): Be smarter here. - // Also consider adding a special case for null-optimized enums, so that we can have - // `Option<&T>: PointerLike` in generic contexts. - exact: !def.is_enum() && !repr.simd(), + let mut layout = NaiveLayout { // An ADT with no inhabited variants should have an uninhabited ABI. abi: NaiveAbi::Uninhabited, ..NaiveLayout::EMPTY }; - let layout = def.variants().iter().try_fold(base, |layout, v| { + let mut empty_variants = 0; + for v in def.variants() { let mut fields = v.fields.iter().map(|f| f.ty(tcx, substs)); let vlayout = univariant(&mut fields, &repr)?; - Ok(layout.union(&vlayout)) - })?; + + if vlayout.size == Size::ZERO && vlayout.exact { + empty_variants += 1; + } else { + // Remember the niches of the last seen variant. + layout.niches = vlayout.niches; + } + + layout = layout.union(&vlayout); + } + + if def.is_enum() { + let may_need_discr = match def.variants().len() { + 0 | 1 => false, + // Simple Option-like niche optimization. + // Handling this special case allows enums like `Option<&T>` + // to be recognized as `PointerLike` and to be transmutable + // in generic contexts. + 2 if empty_variants == 1 && layout.niches == NaiveNiches::Some => { + layout.niches = NaiveNiches::Maybe; // fill up the niche. + false + } + _ => true, + }; + + if may_need_discr || repr.inhibit_enum_layout_opt() { + // For simplicity, assume that the discriminant always get niched. + // This will be wrong in many cases, which will cause the size (and + // sometimes the alignment) to be underestimated. + // FIXME(reference_niches): Be smarter here. + layout.niches = NaiveNiches::Maybe; + layout = layout.inexact(); + } + } else { + assert_eq!(def.variants().len(), 1, "struct should have a single variant"); + + // We don't compute exact alignment for SIMD structs. + if repr.simd() { + layout = layout.inexact(); + } + + // `UnsafeCell` hides all niches. + if def.is_unsafe_cell() { + layout.niches = NaiveNiches::None; + } + } + + let valid_range = tcx.layout_scalar_valid_range(def.did()); + if valid_range != (Bound::Unbounded, Bound::Unbounded) { + let get = |bound, default| match bound { + Bound::Unbounded => default, + Bound::Included(v) => v, + Bound::Excluded(_) => bug!("exclusive `layout_scalar_valid_range` bound"), + }; + + let valid_range = WrappingRange { + start: get(valid_range.0, 0), + // FIXME: this is wrong for scalar-pair ABIs. Fortunately, the + // only type this could currently affect is`NonNull`, + // and the `NaiveNiches` result still ends up correct. + end: get(valid_range.1, layout.size.unsigned_int_max()), + }; + assert!( + valid_range.is_in_range_for(layout.size), + "`layout_scalar_valid_range` values are out of bounds", + ); + if !valid_range.is_full_for(layout.size) { + layout.niches = NaiveNiches::Some; + } + } + layout.pad_to_align(layout.align) } diff --git a/tests/ui/layout/valid_range_oob.stderr b/tests/ui/layout/valid_range_oob.stderr index a3a514fb8309..772113fa5fb2 100644 --- a/tests/ui/layout/valid_range_oob.stderr +++ b/tests/ui/layout/valid_range_oob.stderr @@ -1,6 +1,6 @@ error: the compiler unexpectedly panicked. this is a bug. query stack during panic: -#0 [layout_of] computing layout of `Foo` -#1 [eval_to_allocation_raw] const-evaluating + checking `FOO` +#0 [naive_layout_of] computing layout (naive) of `Foo` +#1 [layout_of] computing layout of `Foo` end of query stack diff --git a/tests/ui/transmute/transmute-fat-pointers.rs b/tests/ui/transmute/transmute-fat-pointers.rs index 7c1beffd14ed..d373ff5f24a5 100644 --- a/tests/ui/transmute/transmute-fat-pointers.rs +++ b/tests/ui/transmute/transmute-fat-pointers.rs @@ -30,4 +30,16 @@ fn f(x: &T) -> &U { unsafe { transmute(x) } //~ ERROR cannot transmute between types of different sizes } +fn g(x: &T) -> Option<&U> { + unsafe { transmute(x) } +} + +fn h(x: &[T]) -> Option<&dyn Send> { + unsafe { transmute(x) } +} + +fn i(x: [usize; 1]) -> Option<&'static T> { + unsafe { transmute(x) } +} + fn main() { } From 634db101eccf03e4b66d10a39a1fdec0bb0f7bbd Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 21 Jul 2023 11:56:49 -0300 Subject: [PATCH 108/113] Implement Stable for ty::Ty --- compiler/rustc_smir/src/rustc_smir/mod.rs | 164 +++++++++++----------- 1 file changed, 84 insertions(+), 80 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index ced9a102c9a8..c9da752ab773 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -64,7 +64,8 @@ impl<'tcx> Context for Tables<'tcx> { } fn ty_kind(&mut self, ty: crate::stable_mir::ty::Ty) -> TyKind { - self.rustc_ty_to_ty(self.types[ty.0]) + let ty = self.types[ty.0]; + ty.stable(self) } } @@ -75,85 +76,6 @@ pub struct Tables<'tcx> { } impl<'tcx> Tables<'tcx> { - fn rustc_ty_to_ty(&mut self, ty: Ty<'tcx>) -> TyKind { - match ty.kind() { - ty::Bool => TyKind::RigidTy(RigidTy::Bool), - ty::Char => TyKind::RigidTy(RigidTy::Char), - ty::Int(int_ty) => match int_ty { - ty::IntTy::Isize => TyKind::RigidTy(RigidTy::Int(IntTy::Isize)), - ty::IntTy::I8 => TyKind::RigidTy(RigidTy::Int(IntTy::I8)), - ty::IntTy::I16 => TyKind::RigidTy(RigidTy::Int(IntTy::I16)), - ty::IntTy::I32 => TyKind::RigidTy(RigidTy::Int(IntTy::I32)), - ty::IntTy::I64 => TyKind::RigidTy(RigidTy::Int(IntTy::I64)), - ty::IntTy::I128 => TyKind::RigidTy(RigidTy::Int(IntTy::I128)), - }, - ty::Uint(uint_ty) => match uint_ty { - ty::UintTy::Usize => TyKind::RigidTy(RigidTy::Uint(UintTy::Usize)), - ty::UintTy::U8 => TyKind::RigidTy(RigidTy::Uint(UintTy::U8)), - ty::UintTy::U16 => TyKind::RigidTy(RigidTy::Uint(UintTy::U16)), - ty::UintTy::U32 => TyKind::RigidTy(RigidTy::Uint(UintTy::U32)), - ty::UintTy::U64 => TyKind::RigidTy(RigidTy::Uint(UintTy::U64)), - ty::UintTy::U128 => TyKind::RigidTy(RigidTy::Uint(UintTy::U128)), - }, - ty::Float(float_ty) => match float_ty { - ty::FloatTy::F32 => TyKind::RigidTy(RigidTy::Float(FloatTy::F32)), - ty::FloatTy::F64 => TyKind::RigidTy(RigidTy::Float(FloatTy::F64)), - }, - ty::Adt(adt_def, generic_args) => TyKind::RigidTy(RigidTy::Adt( - rustc_internal::adt_def(adt_def.did()), - generic_args.stable(self), - )), - ty::Foreign(def_id) => { - TyKind::RigidTy(RigidTy::Foreign(rustc_internal::foreign_def(*def_id))) - } - ty::Str => TyKind::RigidTy(RigidTy::Str), - ty::Array(ty, constant) => { - TyKind::RigidTy(RigidTy::Array(self.intern_ty(*ty), opaque(constant))) - } - ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(self.intern_ty(*ty))), - ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { - TyKind::RigidTy(RigidTy::RawPtr(self.intern_ty(*ty), mutbl.stable(self))) - } - ty::Ref(region, ty, mutbl) => TyKind::RigidTy(RigidTy::Ref( - opaque(region), - self.intern_ty(*ty), - mutbl.stable(self), - )), - ty::FnDef(def_id, generic_args) => TyKind::RigidTy(RigidTy::FnDef( - rustc_internal::fn_def(*def_id), - generic_args.stable(self), - )), - ty::FnPtr(poly_fn_sig) => TyKind::RigidTy(RigidTy::FnPtr(poly_fn_sig.stable(self))), - ty::Dynamic(_, _, _) => todo!(), - ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure( - rustc_internal::closure_def(*def_id), - generic_args.stable(self), - )), - ty::Generator(def_id, generic_args, movability) => TyKind::RigidTy(RigidTy::Generator( - rustc_internal::generator_def(*def_id), - generic_args.stable(self), - match movability { - hir::Movability::Static => Movability::Static, - hir::Movability::Movable => Movability::Movable, - }, - )), - ty::Never => TyKind::RigidTy(RigidTy::Never), - ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple( - fields.iter().map(|ty| self.intern_ty(ty)).collect(), - )), - ty::Alias(_, _) => todo!(), - ty::Param(_) => todo!(), - ty::Bound(_, _) => todo!(), - ty::Placeholder(..) - | ty::GeneratorWitness(_) - | ty::GeneratorWitnessMIR(_, _) - | ty::Infer(_) - | ty::Error(_) => { - unreachable!(); - } - } - } - fn intern_ty(&mut self, ty: Ty<'tcx>) -> stable_mir::ty::Ty { if let Some(id) = self.types.iter().position(|&t| t == ty) { return stable_mir::ty::Ty(id); @@ -676,3 +598,85 @@ impl<'tcx> Stable<'tcx> for ty::BoundVariableKind { } } } + +impl<'tcx> Stable<'tcx> for Ty<'tcx> { + type T = stable_mir::ty::TyKind; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + match self.kind() { + ty::Bool => TyKind::RigidTy(RigidTy::Bool), + ty::Char => TyKind::RigidTy(RigidTy::Char), + ty::Int(int_ty) => match int_ty { + ty::IntTy::Isize => TyKind::RigidTy(RigidTy::Int(IntTy::Isize)), + ty::IntTy::I8 => TyKind::RigidTy(RigidTy::Int(IntTy::I8)), + ty::IntTy::I16 => TyKind::RigidTy(RigidTy::Int(IntTy::I16)), + ty::IntTy::I32 => TyKind::RigidTy(RigidTy::Int(IntTy::I32)), + ty::IntTy::I64 => TyKind::RigidTy(RigidTy::Int(IntTy::I64)), + ty::IntTy::I128 => TyKind::RigidTy(RigidTy::Int(IntTy::I128)), + }, + ty::Uint(uint_ty) => match uint_ty { + ty::UintTy::Usize => TyKind::RigidTy(RigidTy::Uint(UintTy::Usize)), + ty::UintTy::U8 => TyKind::RigidTy(RigidTy::Uint(UintTy::U8)), + ty::UintTy::U16 => TyKind::RigidTy(RigidTy::Uint(UintTy::U16)), + ty::UintTy::U32 => TyKind::RigidTy(RigidTy::Uint(UintTy::U32)), + ty::UintTy::U64 => TyKind::RigidTy(RigidTy::Uint(UintTy::U64)), + ty::UintTy::U128 => TyKind::RigidTy(RigidTy::Uint(UintTy::U128)), + }, + ty::Float(float_ty) => match float_ty { + ty::FloatTy::F32 => TyKind::RigidTy(RigidTy::Float(FloatTy::F32)), + ty::FloatTy::F64 => TyKind::RigidTy(RigidTy::Float(FloatTy::F64)), + }, + ty::Adt(adt_def, generic_args) => TyKind::RigidTy(RigidTy::Adt( + rustc_internal::adt_def(adt_def.did()), + generic_args.stable(tables), + )), + ty::Foreign(def_id) => { + TyKind::RigidTy(RigidTy::Foreign(rustc_internal::foreign_def(*def_id))) + } + ty::Str => TyKind::RigidTy(RigidTy::Str), + ty::Array(ty, constant) => { + TyKind::RigidTy(RigidTy::Array(tables.intern_ty(*ty), opaque(constant))) + } + ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(tables.intern_ty(*ty))), + ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { + TyKind::RigidTy(RigidTy::RawPtr(tables.intern_ty(*ty), mutbl.stable(tables))) + } + ty::Ref(region, ty, mutbl) => TyKind::RigidTy(RigidTy::Ref( + opaque(region), + tables.intern_ty(*ty), + mutbl.stable(tables), + )), + ty::FnDef(def_id, generic_args) => TyKind::RigidTy(RigidTy::FnDef( + rustc_internal::fn_def(*def_id), + generic_args.stable(tables), + )), + ty::FnPtr(poly_fn_sig) => TyKind::RigidTy(RigidTy::FnPtr(poly_fn_sig.stable(tables))), + ty::Dynamic(_, _, _) => todo!(), + ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure( + rustc_internal::closure_def(*def_id), + generic_args.stable(tables), + )), + ty::Generator(def_id, generic_args, movability) => TyKind::RigidTy(RigidTy::Generator( + rustc_internal::generator_def(*def_id), + generic_args.stable(tables), + match movability { + hir::Movability::Static => Movability::Static, + hir::Movability::Movable => Movability::Movable, + }, + )), + ty::Never => TyKind::RigidTy(RigidTy::Never), + ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple( + fields.iter().map(|ty| tables.intern_ty(ty)).collect(), + )), + ty::Alias(_, _) => todo!(), + ty::Param(_) => todo!(), + ty::Bound(_, _) => todo!(), + ty::Placeholder(..) + | ty::GeneratorWitness(_) + | ty::GeneratorWitnessMIR(_, _) + | ty::Infer(_) + | ty::Error(_) => { + unreachable!(); + } + } + } +} From 74b8d324eb77a8f337b35dc68ac91b0c2c06debc Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sun, 29 May 2022 01:10:44 +0200 Subject: [PATCH 109/113] Support `.comment` section like GCC/Clang (`!llvm.ident`) Both GCC and Clang write by default a `.comment` section with compiler information: ```txt $ gcc -c -xc /dev/null && readelf -p '.comment' null.o String dump of section '.comment': [ 1] GCC: (GNU) 11.2.0 $ clang -c -xc /dev/null && readelf -p '.comment' null.o String dump of section '.comment': [ 1] clang version 14.0.1 (https://github.com/llvm/llvm-project.git c62053979489ccb002efe411c3af059addcb5d7d) ``` They also implement the `-Qn` flag to avoid doing so: ```txt $ gcc -Qn -c -xc /dev/null && readelf -p '.comment' null.o readelf: Warning: Section '.comment' was not dumped because it does not exist! $ clang -Qn -c -xc /dev/null && readelf -p '.comment' null.o readelf: Warning: Section '.comment' was not dumped because it does not exist! ``` So far, `rustc` only does it for WebAssembly targets and only when debug info is enabled: ```txt $ echo 'fn main(){}' | rustc --target=wasm32-unknown-unknown --emit=llvm-ir -Cdebuginfo=2 - && grep llvm.ident rust_out.ll !llvm.ident = !{!27} ``` In the RFC part of this PR it was decided to always add the information, which gets us closer to other popular compilers. An opt-out flag like GCC and Clang may be added later on if deemed necessary. Implementation-wise, this covers both `ModuleLlvm::new()` and `ModuleLlvm::new_metadata()` cases by moving the addition to `context::create_module` and adds a few test cases. ThinLTO also sees the `llvm.ident` named metadata duplicated (in temporary outputs), so this deduplicates it like it is done for `wasm.custom_sections`. The tests also check this duplication does not take place. Signed-off-by: Miguel Ojeda --- compiler/rustc_codegen_llvm/src/context.rs | 18 ++++++++++++++++++ .../src/debuginfo/metadata.rs | 15 --------------- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 5 +++++ tests/codegen/llvm-ident.rs | 15 +++++++++++++++ tests/run-make/comment-section/Makefile | 15 +++++++++++++++ tests/run-make/llvm-ident/Makefile | 19 +++++++++++++++++++ 6 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 tests/codegen/llvm-ident.rs create mode 100644 tests/run-make/comment-section/Makefile create mode 100644 tests/run-make/llvm-ident/Makefile diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index e1e0a442845d..5bf641055c56 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -33,6 +33,7 @@ use rustc_target::abi::{ use rustc_target::spec::{HasTargetSpec, RelocModel, Target, TlsModel}; use smallvec::SmallVec; +use libc::c_uint; use std::cell::{Cell, RefCell}; use std::ffi::CStr; use std::str; @@ -349,6 +350,23 @@ pub unsafe fn create_module<'ll>( ); } + // Insert `llvm.ident` metadata. + // + // On the wasm targets it will get hooked up to the "producer" sections + // `processed-by` information. + let rustc_producer = + format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION")); + let name_metadata = llvm::LLVMMDStringInContext( + llcx, + rustc_producer.as_ptr().cast(), + rustc_producer.as_bytes().len() as c_uint, + ); + llvm::LLVMAddNamedMetadataOperand( + llmod, + cstr!("llvm.ident").as_ptr(), + llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1), + ); + llmod } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index c6996f2e16ac..905e0e541a8a 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -888,21 +888,6 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, llvm_gcov_ident.as_ptr(), val); } - // Insert `llvm.ident` metadata on the wasm targets since that will - // get hooked up to the "producer" sections `processed-by` information. - if tcx.sess.target.is_like_wasm { - let name_metadata = llvm::LLVMMDStringInContext( - debug_context.llcontext, - rustc_producer.as_ptr().cast(), - rustc_producer.as_bytes().len() as c_uint, - ); - llvm::LLVMAddNamedMetadataOperand( - debug_context.llmod, - cstr!("llvm.ident").as_ptr(), - llvm::LLVMMDNodeInContext(debug_context.llcontext, &name_metadata, 1), - ); - } - return unit_metadata; }; diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index e5fb6b0953f5..71df17c9ce75 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1366,6 +1366,11 @@ LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M, if (WasmCustomSections) WasmCustomSections->eraseFromParent(); + // `llvm.ident` named metadata also gets duplicated. + auto *llvmIdent = (*MOrErr)->getNamedMetadata("llvm.ident"); + if (llvmIdent) + llvmIdent->eraseFromParent(); + return MOrErr; }; bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target); diff --git a/tests/codegen/llvm-ident.rs b/tests/codegen/llvm-ident.rs new file mode 100644 index 000000000000..927f0d602ad7 --- /dev/null +++ b/tests/codegen/llvm-ident.rs @@ -0,0 +1,15 @@ +// Verifies that the `!llvm.ident` named metadata is emitted. +// +// revisions: NONE OPT DEBUG +// +// [OPT] compile-flags: -Copt-level=2 +// [DEBUG] compile-flags: -Cdebuginfo=2 + +// The named metadata should contain a single metadata node (see +// `LLVMRustPrepareThinLTOImport` for details). +// CHECK: !llvm.ident = !{![[ID:[0-9]+]]} + +// In addition, check that the metadata node has the expected content. +// CHECK: ![[ID]] = !{!"rustc version 1.{{.*}}"} + +fn main() {} diff --git a/tests/run-make/comment-section/Makefile b/tests/run-make/comment-section/Makefile new file mode 100644 index 000000000000..9f810063cc86 --- /dev/null +++ b/tests/run-make/comment-section/Makefile @@ -0,0 +1,15 @@ +include ../tools.mk + +# only-linux + +all: + echo 'fn main(){}' | $(RUSTC) - --emit=link,obj -Csave-temps --target=$(TARGET) + + # Check linked output has a `.comment` section with the expected content. + readelf -p '.comment' $(TMPDIR)/rust_out | $(CGREP) -F 'rustc version 1.' + + # Check all object files (including temporary outputs) have a `.comment` + # section with the expected content. + set -e; for f in $(TMPDIR)/*.o; do \ + readelf -p '.comment' $$f | $(CGREP) -F 'rustc version 1.'; \ + done diff --git a/tests/run-make/llvm-ident/Makefile b/tests/run-make/llvm-ident/Makefile new file mode 100644 index 000000000000..e583e6018e0b --- /dev/null +++ b/tests/run-make/llvm-ident/Makefile @@ -0,0 +1,19 @@ +include ../tools.mk + +# only-linux + +all: + # `-Ccodegen-units=16 -Copt-level=2` is used here to trigger thin LTO + # across codegen units to test deduplication of the named metadata + # (see `LLVMRustPrepareThinLTOImport` for details). + echo 'fn main(){}' | $(RUSTC) - --emit=link,obj -Csave-temps -Ccodegen-units=16 -Copt-level=2 --target=$(TARGET) + + # `llvm-dis` is used here since `--emit=llvm-ir` does not emit LLVM IR + # for temporary outputs. + "$(LLVM_BIN_DIR)"/llvm-dis $(TMPDIR)/*.bc + + # Check LLVM IR files (including temporary outputs) have `!llvm.ident` + # named metadata, reusing the related codegen test. + set -e; for f in $(TMPDIR)/*.ll; do \ + $(LLVM_FILECHECK) --input-file $$f ../../codegen/llvm-ident.rs; \ + done From 6e3932e10d02c68d2013e3f6c889a95ec588f28f Mon Sep 17 00:00:00 2001 From: The Miri Conjob Bot Date: Sat, 22 Jul 2023 06:25:15 +0000 Subject: [PATCH 110/113] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index da89b71ccd53..6f8ad244fa28 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -0646a5d1aa3745cb448db247f6fa432890a1812b +a5e2eca40ec17f17b6641bcc7c069380ac395acf From aa33e8945c37a7a2ae59bba880bffa516bf408b6 Mon Sep 17 00:00:00 2001 From: Eric Mark Martin Date: Mon, 17 Jul 2023 20:46:33 -0400 Subject: [PATCH 111/113] add Alias for smir --- compiler/rustc_smir/src/rustc_internal/mod.rs | 8 ++++++ compiler/rustc_smir/src/rustc_smir/mod.rs | 25 ++++++++++++++++++- compiler/rustc_smir/src/stable_mir/ty.rs | 18 +++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index a918bc981c03..e0cf698acd74 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -47,6 +47,10 @@ pub fn generator_def(did: DefId) -> stable_mir::ty::GeneratorDef { with_tables(|t| t.generator_def(did)) } +pub fn alias_def(did: DefId) -> stable_mir::ty::AliasDef { + with_tables(|t| t.alias_def(did)) +} + pub fn param_def(did: DefId) -> stable_mir::ty::ParamDef { with_tables(|t| t.param_def(did)) } @@ -84,6 +88,10 @@ impl<'tcx> Tables<'tcx> { stable_mir::ty::GeneratorDef(self.create_def_id(did)) } + pub fn alias_def(&mut self, did: DefId) -> stable_mir::ty::AliasDef { + stable_mir::ty::AliasDef(self.create_def_id(did)) + } + pub fn param_def(&mut self, did: DefId) -> stable_mir::ty::ParamDef { stable_mir::ty::ParamDef(self.create_def_id(did)) } diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index c9da752ab773..97bde5c3c191 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -237,6 +237,27 @@ impl<'tcx> Stable<'tcx> for mir::CastKind { } } +impl<'tcx> Stable<'tcx> for ty::AliasKind { + type T = stable_mir::ty::AliasKind; + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + use ty::AliasKind::*; + match self { + Projection => stable_mir::ty::AliasKind::Projection, + Inherent => stable_mir::ty::AliasKind::Inherent, + Opaque => stable_mir::ty::AliasKind::Opaque, + Weak => stable_mir::ty::AliasKind::Weak, + } + } +} + +impl<'tcx> Stable<'tcx> for ty::AliasTy<'tcx> { + type T = stable_mir::ty::AliasTy; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + let ty::AliasTy { args, def_id, .. } = self; + stable_mir::ty::AliasTy { def_id: tables.alias_def(*def_id), args: args.stable(tables) } + } +} + impl<'tcx> Stable<'tcx> for ty::adjustment::PointerCoercion { type T = stable_mir::mir::PointerCoercion; fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { @@ -667,7 +688,9 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> { ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple( fields.iter().map(|ty| tables.intern_ty(ty)).collect(), )), - ty::Alias(_, _) => todo!(), + ty::Alias(alias_kind, alias_ty) => { + TyKind::Alias(alias_kind.stable(tables), alias_ty.stable(tables)) + } ty::Param(_) => todo!(), ty::Bound(_, _) => todo!(), ty::Placeholder(..) diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 2b762eab5ef2..885beeda78c9 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -17,6 +17,7 @@ type Span = Opaque; #[derive(Clone, Debug)] pub enum TyKind { RigidTy(RigidTy), + Alias(AliasKind, AliasTy), } #[derive(Clone, Debug)] @@ -94,6 +95,9 @@ pub struct BrNamedDef(pub(crate) DefId); #[derive(Clone, PartialEq, Eq, Debug)] pub struct AdtDef(pub(crate) DefId); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct AliasDef(pub(crate) DefId); + #[derive(Clone, Debug)] pub struct GenericArgs(pub Vec); @@ -104,6 +108,20 @@ pub enum GenericArgKind { Const(Const), } +#[derive(Clone, Debug)] +pub enum AliasKind { + Projection, + Inherent, + Opaque, + Weak, +} + +#[derive(Clone, Debug)] +pub struct AliasTy { + pub def_id: AliasDef, + pub args: GenericArgs, +} + pub type PolyFnSig = Binder; #[derive(Clone, Debug)] From 7ac0ef9d1164fdb744f3f3c2f4a6893ed06cbe73 Mon Sep 17 00:00:00 2001 From: Eric Mark Martin Date: Sat, 22 Jul 2023 15:37:11 -0400 Subject: [PATCH 112/113] add docs for AliasKind::Inherent --- compiler/rustc_type_ir/src/sty.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index b134845dab04..ec0dbffc22f2 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -39,6 +39,7 @@ pub enum AliasKind { /// A projection `::AssocType`. /// Can get normalized away if monomorphic enough. Projection, + /// An associated type in an inherent `impl` Inherent, /// An opaque type (usually from `impl Trait` in type aliases or function return types) /// Can only be normalized away in RevealAll mode From 32198e1b5c334ca3f70a1f14921e6df17628c9dd Mon Sep 17 00:00:00 2001 From: The Miri Conjob Bot Date: Sat, 22 Jul 2023 06:36:56 +0000 Subject: [PATCH 113/113] fmt --- src/tools/miri/src/shims/mod.rs | 2 +- src/tools/miri/src/shims/unix/fs.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index 0caa9b522f94..63e55e7369ad 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -20,8 +20,8 @@ pub mod tls; use log::trace; use rustc_middle::{mir, ty}; -use rustc_target::spec::abi::Abi; use rustc_target::abi::HasDataLayout as _; +use rustc_target::spec::abi::Abi; use crate::*; use helpers::check_arg_count; diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 5da66801694b..b973a9e4766d 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -12,7 +12,7 @@ use log::trace; use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::TyCtxt; -use rustc_target::abi::{Align, Size, HasDataLayout as _}; +use rustc_target::abi::{Align, HasDataLayout as _, Size}; use crate::shims::os_str::bytes_to_os_str; use crate::*;