From 45791dde8e0d258d794e561b9dff53f1dbb16377 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 24 Nov 2024 01:20:28 +0100 Subject: [PATCH 01/24] Support linker arguments that contain commas --- compiler/rustc_codegen_ssa/src/back/linker.rs | 51 +++++++++++++++---- .../src/back/linker/tests.rs | 25 +++++++++ 2 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 compiler/rustc_codegen_ssa/src/back/linker/tests.rs diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 6ee599c9964c5..162ea38dfd5cd 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -24,6 +24,9 @@ use super::command::Command; use super::symbol_export; use crate::errors; +#[cfg(test)] +mod tests; + /// Disables non-English messages from localized linkers. /// Such messages may cause issues with text encoding on Windows (#35785) /// and prevent inspection of linker output in case of errors, which we occasionally do. @@ -178,23 +181,53 @@ fn verbatim_args( } l } +/// Add underlying linker arguments to C compiler command, by wrapping them in +/// `-Wl` or `-Xlinker`. +fn convert_link_args_to_cc_args( + cmd: &mut Command, + args: impl IntoIterator, IntoIter: ExactSizeIterator>, +) { + let args = args.into_iter(); + if args.len() == 0 { + return; + } + + let mut combined_arg = OsString::from("-Wl"); + for arg in args { + // If the argument itself contains a comma, we need to emit it + // as `-Xlinker`, otherwise we can use `-Wl`. + if arg.as_ref().as_encoded_bytes().contains(&b',') { + // Emit current `-Wl` argument, if any has been built. + if combined_arg != OsStr::new("-Wl") { + cmd.arg(combined_arg); + // Begin next `-Wl` argument. + combined_arg = OsString::from("-Wl"); + } + + // Emit `-Xlinker` argument. + cmd.arg("-Xlinker"); + cmd.arg(arg); + } else { + // Append to `-Wl` argument. + combined_arg.push(","); + combined_arg.push(arg); + } + } + // Emit final `-Wl` argument. + if combined_arg != OsStr::new("-Wl") { + cmd.arg(combined_arg); + } +} /// Arguments for the underlying linker. /// Add options to pass them through cc wrapper if `Linker` is a cc wrapper. fn link_args( l: &mut L, args: impl IntoIterator, IntoIter: ExactSizeIterator>, ) -> &mut L { - let args = args.into_iter(); if !l.is_cc() { verbatim_args(l, args); - } else if args.len() != 0 { - // FIXME: Support arguments with commas, see `rpaths_to_flags` for the example. - let mut combined_arg = OsString::from("-Wl"); - for arg in args { - combined_arg.push(","); - combined_arg.push(arg); - } - l.cmd().arg(combined_arg); + } else { + convert_link_args_to_cc_args(l.cmd(), args); } l } diff --git a/compiler/rustc_codegen_ssa/src/back/linker/tests.rs b/compiler/rustc_codegen_ssa/src/back/linker/tests.rs new file mode 100644 index 0000000000000..293ed6634ae10 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/back/linker/tests.rs @@ -0,0 +1,25 @@ +use super::*; + +#[test] +fn test_xlinker() { + let mut cmd = Command::new("foo"); + convert_link_args_to_cc_args(&mut cmd, &[ + "arg1", + "arg2", + "arg3,with,comma", + "arg4,with,comma", + "arg5", + "arg6,with,comma", + ]); + + assert_eq!(cmd.get_args(), [ + OsStr::new("-Wl,arg1,arg2"), + OsStr::new("-Xlinker"), + OsStr::new("arg3,with,comma"), + OsStr::new("-Xlinker"), + OsStr::new("arg4,with,comma"), + OsStr::new("-Wl,arg5"), + OsStr::new("-Xlinker"), + OsStr::new("arg6,with,comma"), + ]); +} From cb6f8fa422ce14b82b60af79d0f0d0ad80936ecd Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 13 Nov 2024 00:27:14 +0100 Subject: [PATCH 02/24] Support rpath with -Clinker-flavor=ld Using `cc_args` panics when using `-Clinker-flavor=ld`, because the arguments are in a form tailored for `-Clinker-flavor=gcc`. So instead, we use `link_args` and let that wrap the arguments with the appropriate `-Wl` or `-Xlinker` when needed. --- compiler/rustc_codegen_ssa/src/back/link.rs | 4 +-- .../src/back/linker/tests.rs | 7 ++++ compiler/rustc_codegen_ssa/src/back/rpath.rs | 34 ++++++------------- .../rustc_codegen_ssa/src/back/rpath/tests.rs | 23 +------------ 4 files changed, 21 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 5149e3a12f232..ea184bb16aa96 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1384,7 +1384,7 @@ fn link_sanitizer_runtime( let filename = format!("rustc{channel}_rt.{name}"); let path = find_sanitizer_runtime(sess, &filename); let rpath = path.to_str().expect("non-utf8 component in path"); - linker.cc_args(&["-Wl,-rpath", "-Xlinker", rpath]); + linker.link_args(&["-rpath", rpath]); linker.link_dylib_by_name(&filename, false, true); } else if sess.target.is_like_msvc && flavor == LinkerFlavor::Msvc(Lld::No) && name == "asan" { // MSVC provides the `/INFERASANLIBS` argument to automatically find the @@ -2208,7 +2208,7 @@ fn add_rpath_args( is_like_osx: sess.target.is_like_osx, linker_is_gnu: sess.target.linker_flavor.is_gnu(), }; - cmd.cc_args(&rpath::get_rpath_flags(&rpath_config)); + cmd.link_args(&rpath::get_rpath_linker_args(&rpath_config)); } } diff --git a/compiler/rustc_codegen_ssa/src/back/linker/tests.rs b/compiler/rustc_codegen_ssa/src/back/linker/tests.rs index 293ed6634ae10..bf3e8c902009e 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker/tests.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker/tests.rs @@ -1,5 +1,12 @@ use super::*; +#[test] +fn test_rpaths_to_args() { + let mut cmd = Command::new("foo"); + convert_link_args_to_cc_args(&mut cmd, &["-rpath", "path1", "-rpath", "path2"]); + assert_eq!(cmd.get_args(), [OsStr::new("-Wl,-rpath,path1,-rpath,path2")]); +} + #[test] fn test_xlinker() { let mut cmd = Command::new("foo"); diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs index 56a808df6b0bd..d633cc98ac87e 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs @@ -13,39 +13,27 @@ pub(super) struct RPathConfig<'a> { pub linker_is_gnu: bool, } -pub(super) fn get_rpath_flags(config: &RPathConfig<'_>) -> Vec { +pub(super) fn get_rpath_linker_args(config: &RPathConfig<'_>) -> Vec { debug!("preparing the RPATH!"); let rpaths = get_rpaths(config); - let mut flags = rpaths_to_flags(rpaths); + let mut args = Vec::with_capacity(rpaths.len() * 2); // the minimum needed capacity + + for rpath in rpaths { + args.push("-rpath".into()); + args.push(rpath); + } if config.linker_is_gnu { // Use DT_RUNPATH instead of DT_RPATH if available - flags.push("-Wl,--enable-new-dtags".into()); + args.push("--enable-new-dtags".into()); // Set DF_ORIGIN for substitute $ORIGIN - flags.push("-Wl,-z,origin".into()); - } - - flags -} - -fn rpaths_to_flags(rpaths: Vec) -> Vec { - let mut ret = Vec::with_capacity(rpaths.len()); // the minimum needed capacity - - for rpath in rpaths { - if rpath.to_string_lossy().contains(',') { - ret.push("-Wl,-rpath".into()); - ret.push("-Xlinker".into()); - ret.push(rpath); - } else { - let mut single_arg = OsString::from("-Wl,-rpath,"); - single_arg.push(rpath); - ret.push(single_arg); - } + args.push("-z".into()); + args.push("origin".into()); } - ret + args } fn get_rpaths(config: &RPathConfig<'_>) -> Vec { diff --git a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs index 39034842d1086..f1a30105c5908 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs @@ -1,13 +1,4 @@ -use std::ffi::OsString; -use std::path::{Path, PathBuf}; - -use super::{RPathConfig, get_rpath_relative_to_output, minimize_rpaths, rpaths_to_flags}; - -#[test] -fn test_rpaths_to_flags() { - let flags = rpaths_to_flags(vec!["path1".into(), "path2".into()]); - assert_eq!(flags, ["-Wl,-rpath,path1", "-Wl,-rpath,path2"]); -} +use super::*; #[test] fn test_minimize1() { @@ -69,15 +60,3 @@ fn test_rpath_relative_issue_119571() { // Should not panic when lib only contains filename. let _ = get_rpath_relative_to_output(config, Path::new("libstd.so")); } - -#[test] -fn test_xlinker() { - let args = rpaths_to_flags(vec!["a/normal/path".into(), "a,comma,path".into()]); - - assert_eq!(args, vec![ - OsString::from("-Wl,-rpath,a/normal/path"), - OsString::from("-Wl,-rpath"), - OsString::from("-Xlinker"), - OsString::from("a,comma,path") - ]); -} From 6bbf832cf266e48c1e8773e5d64a7e90bd866c7b Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 24 Nov 2024 01:23:19 +0100 Subject: [PATCH 03/24] Remove unnecessary 0 link args optimization --- compiler/rustc_codegen_ssa/src/back/linker.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 162ea38dfd5cd..05dfbd40a0ad6 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -183,15 +183,7 @@ fn verbatim_args( } /// Add underlying linker arguments to C compiler command, by wrapping them in /// `-Wl` or `-Xlinker`. -fn convert_link_args_to_cc_args( - cmd: &mut Command, - args: impl IntoIterator, IntoIter: ExactSizeIterator>, -) { - let args = args.into_iter(); - if args.len() == 0 { - return; - } - +fn convert_link_args_to_cc_args(cmd: &mut Command, args: impl IntoIterator>) { let mut combined_arg = OsString::from("-Wl"); for arg in args { // If the argument itself contains a comma, we need to emit it @@ -220,10 +212,7 @@ fn convert_link_args_to_cc_args( } /// Arguments for the underlying linker. /// Add options to pass them through cc wrapper if `Linker` is a cc wrapper. -fn link_args( - l: &mut L, - args: impl IntoIterator, IntoIter: ExactSizeIterator>, -) -> &mut L { +fn link_args(l: &mut L, args: impl IntoIterator>) -> &mut L { if !l.is_cc() { verbatim_args(l, args); } else { @@ -257,7 +246,7 @@ macro_rules! generate_arg_methods { verbatim_args(self, iter::once(arg)) } #[allow(unused)] - pub(crate) fn link_args(&mut self, args: impl IntoIterator, IntoIter: ExactSizeIterator>) -> &mut Self { + pub(crate) fn link_args(&mut self, args: impl IntoIterator>) -> &mut Self { link_args(self, args) } #[allow(unused)] From 7cf3f8ba7a4a7c8b9dc7a44ccc1702fa7d0d2d1c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 28 Nov 2024 21:22:06 +0100 Subject: [PATCH 04/24] Do not emit `missing_doc_code_examples` rustdoc lint on module and a few other items --- .../passes/check_doc_test_visibility.rs | 11 ++++++-- .../coverage/doc-examples-json.stdout | 2 +- tests/rustdoc-ui/coverage/doc-examples.stdout | 4 +-- tests/rustdoc-ui/coverage/json.stdout | 2 +- tests/rustdoc-ui/lints/check.rs | 1 - tests/rustdoc-ui/lints/check.stderr | 28 ++++++------------- .../rustdoc-ui/lints/doc-without-codeblock.rs | 3 +- .../lints/doc-without-codeblock.stderr | 26 +++-------------- .../lints/lint-missing-doc-code-example.rs | 2 +- .../lint-missing-doc-code-example.stderr | 14 +++------- tests/rustdoc-ui/show-coverage-json.stdout | 2 +- tests/rustdoc-ui/show-coverage.stdout | 4 +-- 12 files changed, 35 insertions(+), 64 deletions(-) diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index bf851b278b82a..c288a3cf2a475 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -60,8 +60,6 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - item.kind, clean::StructFieldItem(_) | clean::VariantItem(_) - | clean::AssocConstItem(..) - | clean::AssocTypeItem(..) | clean::TypeAliasItem(_) | clean::StaticItem(_) | clean::ConstantItem(..) @@ -69,6 +67,15 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - | clean::ImportItem(_) | clean::PrimitiveItem(_) | clean::KeywordItem + | clean::ModuleItem(_) + | clean::TraitAliasItem(_) + | clean::ForeignFunctionItem(..) + | clean::ForeignStaticItem(..) + | clean::ForeignTypeItem + | clean::AssocConstItem(..) + | clean::AssocTypeItem(..) + | clean::TyAssocConstItem(..) + | clean::TyAssocTypeItem(..) // check for trait impl | clean::ImplItem(box clean::Impl { trait_: Some(_), .. }) ) diff --git a/tests/rustdoc-ui/coverage/doc-examples-json.stdout b/tests/rustdoc-ui/coverage/doc-examples-json.stdout index 92f58556975ae..070fed0783e76 100644 --- a/tests/rustdoc-ui/coverage/doc-examples-json.stdout +++ b/tests/rustdoc-ui/coverage/doc-examples-json.stdout @@ -1 +1 @@ -{"$DIR/doc-examples-json.rs":{"total":3,"with_docs":2,"total_examples":2,"with_examples":1}} +{"$DIR/doc-examples-json.rs":{"total":3,"with_docs":2,"total_examples":1,"with_examples":1}} diff --git a/tests/rustdoc-ui/coverage/doc-examples.stdout b/tests/rustdoc-ui/coverage/doc-examples.stdout index 8188740f8739b..793adeb351801 100644 --- a/tests/rustdoc-ui/coverage/doc-examples.stdout +++ b/tests/rustdoc-ui/coverage/doc-examples.stdout @@ -1,7 +1,7 @@ +-------------------------------------+------------+------------+------------+------------+ | File | Documented | Percentage | Examples | Percentage | +-------------------------------------+------------+------------+------------+------------+ -| ...tdoc-ui/coverage/doc-examples.rs | 4 | 100.0% | 1 | 25.0% | +| ...tdoc-ui/coverage/doc-examples.rs | 4 | 100.0% | 1 | 33.3% | +-------------------------------------+------------+------------+------------+------------+ -| Total | 4 | 100.0% | 1 | 25.0% | +| Total | 4 | 100.0% | 1 | 33.3% | +-------------------------------------+------------+------------+------------+------------+ diff --git a/tests/rustdoc-ui/coverage/json.stdout b/tests/rustdoc-ui/coverage/json.stdout index c2be73ce3edd7..25fd896baf1d3 100644 --- a/tests/rustdoc-ui/coverage/json.stdout +++ b/tests/rustdoc-ui/coverage/json.stdout @@ -1 +1 @@ -{"$DIR/json.rs":{"total":17,"with_docs":12,"total_examples":15,"with_examples":6}} +{"$DIR/json.rs":{"total":17,"with_docs":12,"total_examples":13,"with_examples":6}} diff --git a/tests/rustdoc-ui/lints/check.rs b/tests/rustdoc-ui/lints/check.rs index 391ba517077c7..058c5d6c468f3 100644 --- a/tests/rustdoc-ui/lints/check.rs +++ b/tests/rustdoc-ui/lints/check.rs @@ -4,7 +4,6 @@ #![feature(rustdoc_missing_doc_code_examples)] //~^ WARN -//~^^ WARN #![warn(missing_docs)] #![warn(rustdoc::missing_doc_code_examples)] diff --git a/tests/rustdoc-ui/lints/check.stderr b/tests/rustdoc-ui/lints/check.stderr index acdb8128443fd..f1f36e8830d6d 100644 --- a/tests/rustdoc-ui/lints/check.stderr +++ b/tests/rustdoc-ui/lints/check.stderr @@ -4,19 +4,20 @@ warning: missing documentation for the crate LL | / #![feature(rustdoc_missing_doc_code_examples)] LL | | LL | | +LL | | #![warn(missing_docs)] ... | LL | | LL | | pub fn foo() {} | |_______________^ | note: the lint level is defined here - --> $DIR/check.rs:9:9 + --> $DIR/check.rs:8:9 | LL | #![warn(missing_docs)] | ^^^^^^^^^^^^ warning: missing documentation for a function - --> $DIR/check.rs:13:1 + --> $DIR/check.rs:12:1 | LL | pub fn foo() {} | ^^^^^^^^^^^^ @@ -26,34 +27,23 @@ warning: no documentation found for this crate's top-level module = help: The following guide may be of use: https://doc.rust-lang.org/$CHANNEL/rustdoc/how-to-write-documentation.html note: the lint level is defined here - --> $DIR/check.rs:11:9 + --> $DIR/check.rs:10:9 | LL | #![warn(rustdoc::all)] | ^^^^^^^^^^^^ = note: `#[warn(rustdoc::missing_crate_level_docs)]` implied by `#[warn(rustdoc::all)]` warning: missing code example in this documentation - --> $DIR/check.rs:5:1 + --> $DIR/check.rs:12:1 | -LL | / #![feature(rustdoc_missing_doc_code_examples)] -LL | | -LL | | -... | -LL | | -LL | | pub fn foo() {} - | |_______________^ +LL | pub fn foo() {} + | ^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/check.rs:10:9 + --> $DIR/check.rs:9:9 | LL | #![warn(rustdoc::missing_doc_code_examples)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: missing code example in this documentation - --> $DIR/check.rs:13:1 - | -LL | pub fn foo() {} - | ^^^^^^^^^^^^^^^ - -warning: 5 warnings emitted +warning: 4 warnings emitted diff --git a/tests/rustdoc-ui/lints/doc-without-codeblock.rs b/tests/rustdoc-ui/lints/doc-without-codeblock.rs index 86d7c83d33598..ccb241d4037e2 100644 --- a/tests/rustdoc-ui/lints/doc-without-codeblock.rs +++ b/tests/rustdoc-ui/lints/doc-without-codeblock.rs @@ -1,4 +1,4 @@ -#![feature(rustdoc_missing_doc_code_examples)] //~ ERROR missing code example in this documentation +#![feature(rustdoc_missing_doc_code_examples)] #![deny(rustdoc::missing_doc_code_examples)] /// Some docs. @@ -6,7 +6,6 @@ pub struct Foo; /// And then, the princess died. -//~^ ERROR missing code example in this documentation pub mod foo { /// Or maybe not because she saved herself! //~^ ERROR missing code example in this documentation diff --git a/tests/rustdoc-ui/lints/doc-without-codeblock.stderr b/tests/rustdoc-ui/lints/doc-without-codeblock.stderr index ebf2a2d54f75c..d230f1606935c 100644 --- a/tests/rustdoc-ui/lints/doc-without-codeblock.stderr +++ b/tests/rustdoc-ui/lints/doc-without-codeblock.stderr @@ -1,14 +1,8 @@ error: missing code example in this documentation - --> $DIR/doc-without-codeblock.rs:1:1 + --> $DIR/doc-without-codeblock.rs:10:5 | -LL | / #![feature(rustdoc_missing_doc_code_examples)] -LL | | #![deny(rustdoc::missing_doc_code_examples)] -LL | | -LL | | /// Some docs. -... | -LL | | } -LL | | } - | |_^ +LL | /// Or maybe not because she saved herself! + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/doc-without-codeblock.rs:2:9 @@ -16,23 +10,11 @@ note: the lint level is defined here LL | #![deny(rustdoc::missing_doc_code_examples)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: missing code example in this documentation - --> $DIR/doc-without-codeblock.rs:8:1 - | -LL | /// And then, the princess died. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: missing code example in this documentation - --> $DIR/doc-without-codeblock.rs:11:5 - | -LL | /// Or maybe not because she saved herself! - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: missing code example in this documentation --> $DIR/doc-without-codeblock.rs:4:1 | LL | /// Some docs. | ^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors diff --git a/tests/rustdoc-ui/lints/lint-missing-doc-code-example.rs b/tests/rustdoc-ui/lints/lint-missing-doc-code-example.rs index 40f35728d79b0..8e5c31d50edc7 100644 --- a/tests/rustdoc-ui/lints/lint-missing-doc-code-example.rs +++ b/tests/rustdoc-ui/lints/lint-missing-doc-code-example.rs @@ -17,7 +17,7 @@ pub fn test() { } #[allow(missing_docs)] -pub mod module1 { //~ ERROR +pub mod module1 { } #[allow(rustdoc::missing_doc_code_examples)] diff --git a/tests/rustdoc-ui/lints/lint-missing-doc-code-example.stderr b/tests/rustdoc-ui/lints/lint-missing-doc-code-example.stderr index f9331250154d7..22533b9816a74 100644 --- a/tests/rustdoc-ui/lints/lint-missing-doc-code-example.stderr +++ b/tests/rustdoc-ui/lints/lint-missing-doc-code-example.stderr @@ -1,8 +1,8 @@ error: missing code example in this documentation - --> $DIR/lint-missing-doc-code-example.rs:20:1 + --> $DIR/lint-missing-doc-code-example.rs:38:3 | -LL | pub mod module1 { - | ^^^^^^^^^^^^^^^ +LL | /// doc + | ^^^^^^^ | note: the lint level is defined here --> $DIR/lint-missing-doc-code-example.rs:3:9 @@ -10,12 +10,6 @@ note: the lint level is defined here LL | #![deny(rustdoc::missing_doc_code_examples)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: missing code example in this documentation - --> $DIR/lint-missing-doc-code-example.rs:38:3 - | -LL | /// doc - | ^^^^^^^ - error: missing code example in this documentation --> $DIR/lint-missing-doc-code-example.rs:50:1 | @@ -34,5 +28,5 @@ error: missing code example in this documentation LL | /// Doc | ^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/rustdoc-ui/show-coverage-json.stdout b/tests/rustdoc-ui/show-coverage-json.stdout index ed5b5a60212eb..e6e882b2c57f8 100644 --- a/tests/rustdoc-ui/show-coverage-json.stdout +++ b/tests/rustdoc-ui/show-coverage-json.stdout @@ -1 +1 @@ -{"$DIR/show-coverage-json.rs":{"total":2,"with_docs":1,"total_examples":2,"with_examples":1}} +{"$DIR/show-coverage-json.rs":{"total":2,"with_docs":1,"total_examples":1,"with_examples":1}} diff --git a/tests/rustdoc-ui/show-coverage.stdout b/tests/rustdoc-ui/show-coverage.stdout index b3b7679771f22..b9e0316545e77 100644 --- a/tests/rustdoc-ui/show-coverage.stdout +++ b/tests/rustdoc-ui/show-coverage.stdout @@ -1,7 +1,7 @@ +-------------------------------------+------------+------------+------------+------------+ | File | Documented | Percentage | Examples | Percentage | +-------------------------------------+------------+------------+------------+------------+ -| ...ests/rustdoc-ui/show-coverage.rs | 1 | 50.0% | 1 | 50.0% | +| ...ests/rustdoc-ui/show-coverage.rs | 1 | 50.0% | 1 | 100.0% | +-------------------------------------+------------+------------+------------+------------+ -| Total | 1 | 50.0% | 1 | 50.0% | +| Total | 1 | 50.0% | 1 | 100.0% | +-------------------------------------+------------+------------+------------+------------+ From 9142cae7d5b86ac70f815c32c0f3b1223b62a947 Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 8 Aug 2024 10:20:40 +0200 Subject: [PATCH 05/24] add `LinkageInfo` to keep track of how we figured out the linkage --- .../rustc_codegen_cranelift/src/driver/mod.rs | 2 +- compiler/rustc_codegen_gcc/src/base.rs | 2 +- compiler/rustc_codegen_llvm/src/base.rs | 2 +- .../src/back/symbol_export.rs | 4 +- compiler/rustc_codegen_ssa/src/mono_item.rs | 12 ++--- compiler/rustc_middle/src/mir/mono.rs | 46 ++++++++++++++++- .../rustc_monomorphize/src/partitioning.rs | 49 ++++++++++--------- 7 files changed, 82 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/driver/mod.rs b/compiler/rustc_codegen_cranelift/src/driver/mod.rs index fb0eed07c1971..dbc913528f44a 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/mod.rs @@ -30,7 +30,7 @@ fn predefine_mono_items<'tcx>( get_function_sig(tcx, module.target_config().default_call_conv, instance); let linkage = crate::linkage::get_clif_linkage( mono_item, - data.linkage, + data.linkage_info.into_linkage(), data.visibility, is_compiler_builtins, ); diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 18aa32754e1d9..844871a714671 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -213,7 +213,7 @@ pub fn compile_codegen_unit( let mono_items = cgu.items_in_deterministic_order(tcx); for &(mono_item, data) in &mono_items { - mono_item.predefine::>(&cx, data.linkage, data.visibility); + mono_item.predefine::>(&cx, data.linkage_info, data.visibility); } // ... and now that we have everything pre-defined, fill out those definitions. diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index f62310bd94808..82c3cfe9aa04a 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -86,7 +86,7 @@ pub(crate) fn compile_codegen_unit( let cx = CodegenCx::new(tcx, cgu, &llvm_module); let mono_items = cx.codegen_unit.items_in_deterministic_order(cx.tcx); for &(mono_item, data) in &mono_items { - mono_item.predefine::>(&cx, data.linkage, data.visibility); + mono_item.predefine::>(&cx, data.linkage_info, data.visibility); } // ... and now that we have everything pre-defined, fill out those definitions. diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 788a8a13b3ee4..0321a244f8683 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -283,7 +283,7 @@ fn exported_symbols_provider_local( } if tcx.local_crate_exports_generics() { - use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility}; + use rustc_middle::mir::mono::{MonoItem, Visibility}; use rustc_middle::ty::InstanceKind; // Normally, we require that shared monomorphizations are not hidden, @@ -298,7 +298,7 @@ fn exported_symbols_provider_local( // The symbols created in this loop are sorted below it #[allow(rustc::potential_query_instability)] for (mono_item, data) in cgus.iter().flat_map(|cgu| cgu.items().iter()) { - if data.linkage != Linkage::External { + if !data.linkage_info.is_external() { // We can only re-use things with external linkage, otherwise // we'll get a linker error continue; diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 038c5857face1..04159422e3cb2 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -1,8 +1,8 @@ use rustc_hir as hir; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility}; -use rustc_middle::ty::Instance; +use rustc_middle::mir::mono::{LinkageInfo, MonoItem, Visibility}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_middle::ty::Instance; use rustc_middle::{span_bug, ty}; use tracing::debug; @@ -14,7 +14,7 @@ pub trait MonoItemExt<'a, 'tcx> { fn predefine>( &self, cx: &'a Bx::CodegenCx, - linkage: Linkage, + linkage_info: LinkageInfo, visibility: Visibility, ); fn to_raw_string(&self) -> String; @@ -116,7 +116,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { fn predefine>( &self, cx: &'a Bx::CodegenCx, - linkage: Linkage, + linkage_info: LinkageInfo, visibility: Visibility, ) { debug!( @@ -132,10 +132,10 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { match *self { MonoItem::Static(def_id) => { - cx.predefine_static(def_id, linkage, visibility, symbol_name); + cx.predefine_static(def_id, linkage_info.into_linkage(), visibility, symbol_name); } MonoItem::Fn(instance) => { - cx.predefine_fn(instance, linkage, visibility, symbol_name); + cx.predefine_fn(instance, linkage_info.into_linkage(), visibility, symbol_name); } MonoItem::GlobalAsm(..) => {} } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 161716610fe63..ca230d7fa8584 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -266,13 +266,57 @@ pub struct MonoItemData { /// `GloballyShared` maps to `false` and `LocalCopy` maps to `true`. pub inlined: bool, - pub linkage: Linkage, + pub linkage_info: LinkageInfo, pub visibility: Visibility, /// A cached copy of the result of `MonoItem::size_estimate`. pub size_estimate: usize, } +/// Stores how we know what linkage to use +#[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] +pub enum LinkageInfo { + /// The linkage was specified explicitly (e.g. using #[linkage = "..."]) + Explicit(Linkage), + /// Assume the symbol may be used from other CGUs, a safe default + ImplicitExternal, + /// We did not find any uses from other CGUs, so it's fine to make this internal + ImplicitInternal, +} + +impl LinkageInfo { + pub const fn is_external(self) -> bool { + matches!(self.into_linkage(), Linkage::External) + } + + pub const fn into_linkage(self) -> Linkage { + match self { + Self::Explicit(linkage) => linkage, + Self::ImplicitExternal => Linkage::External, + Self::ImplicitInternal => Linkage::Internal, + } + } + + /// Linkage when the MonoItem is a naked function + /// + /// Naked functions are generated as a separate declaration (effectively an extern fn) and + /// definition (using global assembly). To link them together, some flavor of external linkage + /// must be used. + pub const fn into_naked_linkage(self) -> Linkage { + match self { + // promote Weak linkage to ExternalWeak + Self::Explicit(Linkage::WeakAny | Linkage::WeakODR) => Linkage::ExternalWeak, + Self::Explicit(linkage) => linkage, + + // the "implicit" means that linkage is picked by the partitioning algorithm. + // picking external should always be valid (given that we are in fact linking + // to the global assembly in the same CGU) + Self::ImplicitExternal => Linkage::External, + Self::ImplicitInternal => Linkage::External, + } + } +} + /// Specifies the linkage type for a `MonoItem`. /// /// See for more details about these variants. diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 7ea4ded2b052c..2fd89e389e547 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -109,8 +109,8 @@ use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; use rustc_middle::mir::mono::{ - CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, MonoItem, MonoItemData, - Visibility, + CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, LinkageInfo, MonoItem, + MonoItemData, Visibility, }; use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths}; use rustc_middle::ty::visit::TypeVisitableExt; @@ -245,7 +245,7 @@ where let cgu = codegen_units.entry(cgu_name).or_insert_with(|| CodegenUnit::new(cgu_name)); let mut can_be_internalized = true; - let (linkage, visibility) = mono_item_linkage_and_visibility( + let (linkage_info, visibility) = mono_item_linkage_info_and_visibility( cx.tcx, &mono_item, &mut can_be_internalized, @@ -259,7 +259,7 @@ where cgu.items_mut().insert(mono_item, MonoItemData { inlined: false, - linkage, + linkage_info, visibility, size_estimate, }); @@ -278,7 +278,7 @@ where // This is a CGU-private copy. cgu.items_mut().entry(inlined_item).or_insert_with(|| MonoItemData { inlined: true, - linkage: Linkage::Internal, + linkage_info: LinkageInfo::ImplicitInternal, visibility: Visibility::Default, size_estimate: inlined_item.size_estimate(cx.tcx), }); @@ -589,7 +589,8 @@ fn internalize_symbols<'tcx>( // If we got here, we did not find any uses from other CGUs, so // it's fine to make this monomorphization internal. - data.linkage = Linkage::Internal; + debug_assert_eq!(data.linkage_info, LinkageInfo::ImplicitExternal); + data.linkage_info = LinkageInfo::ImplicitInternal; data.visibility = Visibility::Default; } } @@ -607,7 +608,7 @@ fn mark_code_coverage_dead_code_cgu<'tcx>(codegen_units: &mut [CodegenUnit<'tcx> // function symbols to be included via `-u` or `/include` linker args. let dead_code_cgu = codegen_units .iter_mut() - .filter(|cgu| cgu.items().iter().any(|(_, data)| data.linkage == Linkage::External)) + .filter(|cgu| cgu.items().iter().any(|(_, data)| data.linkage_info.is_external())) .min_by_key(|cgu| cgu.size_estimate()); // If there are no CGUs that have externally linked items, then we just @@ -736,24 +737,26 @@ fn fallback_cgu_name(name_builder: &mut CodegenUnitNameBuilder<'_>) -> Symbol { name_builder.build_cgu_name(LOCAL_CRATE, &["fallback"], Some("cgu")) } -fn mono_item_linkage_and_visibility<'tcx>( +fn mono_item_linkage_info_and_visibility<'tcx>( tcx: TyCtxt<'tcx>, mono_item: &MonoItem<'tcx>, can_be_internalized: &mut bool, can_export_generics: bool, always_export_generics: bool, -) -> (Linkage, Visibility) { +) -> (LinkageInfo, Visibility) { if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) { - return (explicit_linkage, Visibility::Default); + (LinkageInfo::Explicit(explicit_linkage), Visibility::Default) + } else { + let vis = mono_item_visibility( + tcx, + mono_item, + can_be_internalized, + can_export_generics, + always_export_generics, + ); + + (LinkageInfo::ImplicitExternal, vis) } - let vis = mono_item_visibility( - tcx, - mono_item, - can_be_internalized, - can_export_generics, - always_export_generics, - ); - (Linkage::External, vis) } type CguNameCache = UnordMap<(DefId, bool), Symbol>; @@ -1033,7 +1036,7 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit< writeln!(s, " - items: {num_items}, mean size: {mean_size:.1}, sizes: {sizes}",); for (item, data) in cgu.items_in_deterministic_order(tcx) { - let linkage = data.linkage; + let linkage_info = data.linkage_info; let symbol_name = item.symbol_name(tcx).name; let symbol_hash_start = symbol_name.rfind('h'); let symbol_hash = symbol_hash_start.map_or("", |i| &symbol_name[i..]); @@ -1041,7 +1044,7 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit< let size = data.size_estimate; let _ = with_no_trimmed_paths!(writeln!( s, - " - {item} [{linkage:?}] [{symbol_hash}] ({kind}, size: {size})" + " - {item} [{linkage_info:?}] [{symbol_hash}] ({kind}, size: {size})" )); } @@ -1194,7 +1197,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co for cgu in codegen_units { for (&mono_item, &data) in cgu.items() { - item_to_cgus.entry(mono_item).or_default().push((cgu.name(), data.linkage)); + item_to_cgus.entry(mono_item).or_default().push((cgu.name(), data.linkage_info)); } } @@ -1207,11 +1210,11 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty); cgus.sort_by_key(|(name, _)| *name); cgus.dedup(); - for &(ref cgu_name, linkage) in cgus.iter() { + for &(ref cgu_name, linkage_info) in cgus.iter() { output.push(' '); output.push_str(cgu_name.as_str()); - let linkage_abbrev = match linkage { + let linkage_abbrev = match linkage_info.into_linkage() { Linkage::External => "External", Linkage::AvailableExternally => "Available", Linkage::LinkOnceAny => "OnceAny", From bdf64e134d2d55919c567c891ddc00b757fcd27c Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 19 Jul 2024 23:04:32 +0200 Subject: [PATCH 06/24] squashed changes to inlining and const eval first steps of codegen for `#[naked]` functions using `global_asm!` configure external linkage if no linkage is explicitly set create a `FunctionCx` and properly evaluate consts inline attribute is no longer relevant for naked functions the naked attribute no longer needs to be set by llvm/... we're generating global asm now, so this attribute is meaningless for the codegen backend --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 11 +- compiler/rustc_codegen_ssa/src/mir/mod.rs | 27 +++ .../rustc_codegen_ssa/src/mir/naked_asm.rs | 212 ++++++++++++++++++ tests/codegen/naked-fn/naked-noinline.rs | 31 --- 4 files changed, 248 insertions(+), 33 deletions(-) create mode 100644 compiler/rustc_codegen_ssa/src/mir/naked_asm.rs delete mode 100644 tests/codegen/naked-fn/naked-noinline.rs diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index a5acd8170ab81..0768b01cb15e5 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -113,7 +113,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { sym::rustc_allocator_zeroed => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED } - sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, + sym::naked => { + // this attribute is ignored during codegen, because a function marked as naked is + // turned into a global asm block. + } sym::no_mangle => { if tcx.opt_item_name(did.to_def_id()).is_some() { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE @@ -627,7 +630,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { - codegen_fn_attrs.inline = InlineAttr::Never; + // naked functions are generated using an extern function and global assembly. To + // make sure that these can be linked together by LLVM, the linkage should be external, + // unless the user explicitly configured something else (in which case any linker errors + // they encounter are their responsibility). + codegen_fn_attrs.linkage = codegen_fn_attrs.linkage.or(Some(Linkage::External)); } // Weak lang items have the same semantics as "std internal" symbols in the diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 0cbc5c45736e8..b14e43acb6078 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -20,6 +20,7 @@ mod coverageinfo; pub mod debuginfo; mod intrinsic; mod locals; +mod naked_asm; pub mod operand; pub mod place; mod rvalue; @@ -176,6 +177,32 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); debug!("fn_abi: {:?}", fn_abi); + if cx.tcx().has_attr(instance.def.def_id(), rustc_span::sym::naked) { + let cached_llbbs = IndexVec::new(); + + let fx: FunctionCx<'_, '_, Bx> = FunctionCx { + instance, + mir, + llfn, + fn_abi, + cx, + personality_slot: None, + cached_llbbs, + unreachable_block: None, + terminate_block: None, + cleanup_kinds: None, + landing_pads: IndexVec::from_elem(None, &mir.basic_blocks), + funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()), + locals: locals::Locals::empty(), + debug_context: None, + per_local_var_debug_info: None, + caller_location: None, + }; + + fx.codegen_naked_asm(instance); + return; + } + let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, mir); let start_llbb = Bx::append_block(cx, llfn, "start"); diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs new file mode 100644 index 0000000000000..1f41a9aefeb73 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -0,0 +1,212 @@ +use crate::common; +use crate::mir::FunctionCx; +use crate::traits::{AsmMethods, BuilderMethods, GlobalAsmOperandRef}; +use rustc_middle::bug; +use rustc_middle::mir::InlineAsmOperand; +use rustc_middle::ty; +use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_middle::ty::{Instance, TyCtxt}; + +use rustc_span::sym; +use rustc_target::asm::InlineAsmArch; + +impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + pub fn codegen_naked_asm(&self, instance: Instance<'tcx>) { + let cx = &self.cx; + + let rustc_middle::mir::TerminatorKind::InlineAsm { + asm_macro: _, + template, + ref operands, + options, + line_spans, + targets: _, + unwind: _, + } = self.mir.basic_blocks.iter().next().unwrap().terminator().kind + else { + bug!("#[naked] functions should always terminate with an asm! block") + }; + + let operands: Vec<_> = + operands.iter().map(|op| self.inline_to_global_operand(op)).collect(); + + let (begin, end) = crate::mir::naked_asm::prefix_and_suffix(cx.tcx(), instance); + + let mut template_vec = Vec::new(); + template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin)); + template_vec.extend(template.iter().cloned()); + template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(end)); + + cx.codegen_global_asm(&template_vec, &operands, options, line_spans); + } + + fn inline_to_global_operand(&self, op: &InlineAsmOperand<'tcx>) -> GlobalAsmOperandRef<'tcx> { + match op { + InlineAsmOperand::Const { value } => { + let const_value = self.eval_mir_constant(value); + let string = common::asm_const_to_str( + self.cx.tcx(), + value.span, + const_value, + self.cx.layout_of(value.ty()), + ); + GlobalAsmOperandRef::Const { string } + } + InlineAsmOperand::SymFn { value } => { + let instance = match value.ty().kind() { + &ty::FnDef(def_id, args) => Instance::new(def_id, args), + _ => bug!("asm sym is not a function"), + }; + + GlobalAsmOperandRef::SymFn { instance } + } + InlineAsmOperand::SymStatic { def_id } => { + GlobalAsmOperandRef::SymStatic { def_id: *def_id } + } + InlineAsmOperand::In { .. } + | InlineAsmOperand::Out { .. } + | InlineAsmOperand::InOut { .. } + | InlineAsmOperand::Label { .. } => { + bug!("invalid operand type for naked_asm!") + } + } + } +} + +enum AsmBinaryFormat { + Elf, + Macho, + Coff, +} + +impl AsmBinaryFormat { + fn from_target(target: &rustc_target::spec::Target) -> Self { + if target.is_like_windows { + Self::Coff + } else if target.options.vendor == "apple" { + Self::Macho + } else { + Self::Elf + } + } +} + +fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (String, String) { + use std::fmt::Write; + + let target = &tcx.sess.target; + let target_arch = tcx.sess.asm_arch; + + let is_arm = target.arch == "arm"; + let is_thumb = is_arm && target.llvm_target.contains("thumb"); + + let mangle = (target.is_like_windows && matches!(target_arch, Some(InlineAsmArch::X86))) + || target.options.vendor == "apple"; + + let asm_name = format!("{}{}", if mangle { "_" } else { "" }, tcx.symbol_name(instance).name); + + let opt_section = tcx + .get_attr(instance.def.def_id(), sym::link_section) + .and_then(|attr| attr.value_str()) + .map(|attr| attr.as_str().to_string()); + + let instruction_set = + tcx.get_attr(instance.def.def_id(), sym::instruction_set).and_then(|attr| attr.value_str()); + + let (arch_prefix, arch_suffix) = if is_arm { + ( + match instruction_set { + None => match is_thumb { + true => ".thumb\n.thumb_func", + false => ".arm", + }, + Some(sym::a32) => ".arm", + Some(sym::t32) => ".thumb\n.thumb_func", + Some(other) => bug!("invalid instruction set: {other}"), + }, + match is_thumb { + true => ".thumb", + false => ".arm", + }, + ) + } else { + ("", "") + }; + + let mut begin = String::new(); + let mut end = String::new(); + match AsmBinaryFormat::from_target(&tcx.sess.target) { + AsmBinaryFormat::Elf => { + let section = opt_section.unwrap_or(format!(".text.{asm_name}")); + + let progbits = match is_arm { + true => "%progbits", + false => "@progbits", + }; + + let function = match is_arm { + true => "%function", + false => "@function", + }; + + writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap(); + writeln!(begin, ".balign 4").unwrap(); + writeln!(begin, ".globl {asm_name}").unwrap(); + writeln!(begin, ".hidden {asm_name}").unwrap(); + writeln!(begin, ".type {asm_name}, {function}").unwrap(); + if let Some(instruction_set) = instruction_set { + writeln!(begin, "{}", instruction_set.as_str()).unwrap(); + } + if !arch_prefix.is_empty() { + writeln!(begin, "{}", arch_prefix).unwrap(); + } + writeln!(begin, "{asm_name}:").unwrap(); + + writeln!(end).unwrap(); + writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap(); + writeln!(end, ".popsection").unwrap(); + if !arch_suffix.is_empty() { + writeln!(end, "{}", arch_suffix).unwrap(); + } + } + AsmBinaryFormat::Macho => { + let section = opt_section.unwrap_or("__TEXT,__text".to_string()); + writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap(); + writeln!(begin, ".balign 4").unwrap(); + writeln!(begin, ".globl {asm_name}").unwrap(); + writeln!(begin, ".private_extern {asm_name}").unwrap(); + if let Some(instruction_set) = instruction_set { + writeln!(begin, "{}", instruction_set.as_str()).unwrap(); + } + writeln!(begin, "{asm_name}:").unwrap(); + + writeln!(end).unwrap(); + writeln!(end, ".popsection").unwrap(); + if !arch_suffix.is_empty() { + writeln!(end, "{}", arch_suffix).unwrap(); + } + } + AsmBinaryFormat::Coff => { + let section = opt_section.unwrap_or(format!(".text.{asm_name}")); + writeln!(begin, ".pushsection {},\"xr\"", section).unwrap(); + writeln!(begin, ".balign 4").unwrap(); + writeln!(begin, ".globl {asm_name}").unwrap(); + writeln!(begin, ".def {asm_name}").unwrap(); + writeln!(begin, ".scl 2").unwrap(); + writeln!(begin, ".type 32").unwrap(); + writeln!(begin, ".endef {asm_name}").unwrap(); + if let Some(instruction_set) = instruction_set { + writeln!(begin, "{}", instruction_set.as_str()).unwrap(); + } + writeln!(begin, "{asm_name}:").unwrap(); + + writeln!(end).unwrap(); + writeln!(end, ".popsection").unwrap(); + if !arch_suffix.is_empty() { + writeln!(end, "{}", arch_suffix).unwrap(); + } + } + } + + (begin, end) +} diff --git a/tests/codegen/naked-fn/naked-noinline.rs b/tests/codegen/naked-fn/naked-noinline.rs deleted file mode 100644 index 6ea36d9678315..0000000000000 --- a/tests/codegen/naked-fn/naked-noinline.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Checks that naked functions are never inlined. -//@ compile-flags: -O -Zmir-opt-level=3 -//@ needs-asm-support -//@ ignore-wasm32 -#![crate_type = "lib"] -#![feature(naked_functions)] - -use std::arch::naked_asm; - -#[naked] -#[no_mangle] -pub unsafe extern "C" fn f() { - // Check that f has naked and noinline attributes. - // - // CHECK: define {{(dso_local )?}}void @f() unnamed_addr [[ATTR:#[0-9]+]] - // CHECK-NEXT: start: - // CHECK-NEXT: call void asm - naked_asm!(""); -} - -#[no_mangle] -pub unsafe fn g() { - // Check that call to f is not inlined. - // - // CHECK-LABEL: define {{(dso_local )?}}void @g() - // CHECK-NEXT: start: - // CHECK-NEXT: call void @f() - f(); -} - -// CHECK: attributes [[ATTR]] = { naked{{.*}}noinline{{.*}} } From d67a5c0b25e8fd02f2397d06c5857f96314e6e90 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 20 Jul 2024 01:16:49 +0200 Subject: [PATCH 07/24] test the new global asm output of naked functions --- tests/codegen/naked-fn/naked-functions.rs | 28 +++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/tests/codegen/naked-fn/naked-functions.rs b/tests/codegen/naked-fn/naked-functions.rs index 3f7447af599ab..00d64089ad8bc 100644 --- a/tests/codegen/naked-fn/naked-functions.rs +++ b/tests/codegen/naked-fn/naked-functions.rs @@ -6,8 +6,17 @@ #![feature(naked_functions)] use std::arch::naked_asm; -// CHECK: Function Attrs: naked -// CHECK-NEXT: define{{.*}}void @naked_empty() +// CHECK: module asm ".intel_syntax" +// CHECK: .pushsection .text.naked_empty,\22ax\22, @progbits +// CHECK: .balign 4" +// CHECK: .globl naked_empty" +// CHECK: .hidden naked_empty" +// CHECK: .type naked_empty, @function" +// CHECK-LABEL: naked_empty: +// CHECK: ret +// CHECK: .popsection +// CHECK: .att_syntax + #[no_mangle] #[naked] pub unsafe extern "C" fn naked_empty() { @@ -17,8 +26,19 @@ pub unsafe extern "C" fn naked_empty() { naked_asm!("ret"); } -// CHECK: Function Attrs: naked -// CHECK-NEXT: define{{.*}}i{{[0-9]+}} @naked_with_args_and_return(i64 %0, i64 %1) +// CHECK: .intel_syntax +// CHECK: .pushsection .text.naked_with_args_and_return,\22ax\22, @progbits +// CHECK: .balign 4 +// CHECK: .globl naked_with_args_and_return +// CHECK: .hidden naked_with_args_and_return +// CHECK: .type naked_with_args_and_return, @function +// CHECK-LABEL: naked_with_args_and_return: +// CHECK: lea rax, [rdi + rsi] +// CHECK: ret +// CHECK: .size naked_with_args_and_return, . - naked_with_args_and_return +// CHECK: .popsection +// CHECK: .att_syntax + #[no_mangle] #[naked] pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { From a93192ca85072a41a4f970275d118bde0b3117d5 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 20 Jul 2024 13:17:31 +0200 Subject: [PATCH 08/24] add `InstructionSetAttr::as_str` and make the type `Copy` --- compiler/rustc_attr/src/builtin.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 94f9727eb7fbe..c8928097eafda 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -49,12 +49,21 @@ pub enum InlineAttr { Never, } -#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)] +#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)] pub enum InstructionSetAttr { ArmA32, ArmT32, } +impl InstructionSetAttr { + pub fn as_str(self) -> &'static str { + match self { + Self::ArmA32 => sym::a32.as_str(), + Self::ArmT32 => sym::t32.as_str(), + } + } +} + #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub enum OptimizeAttr { None, From 49a297ab211affaf782cc7603bc413cb8e1ab715 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 20 Jul 2024 13:23:34 +0200 Subject: [PATCH 09/24] squashed changes to tests correctly emit `.hidden` this test was added in https://github.com/rust-lang/rust/pull/105193 but actually NO_COVERAGE is no longer a thing in the compiler. Sadly, the link to the issue is broken, so I don't know what the problem was originally, but I don't think this is relevant any more with the global asm approach rename test file because it now specifically checks for directives only used by non-macos, non-windows x86_64 add codegen tests for 4 interesting platforms add codegen test for the `#[instruction_set]` attribute add test for `#[link_section]` use `tcx.codegen_fn_attrs` to get attribute info Fix #124375 inline const monomorphization/evaluation getting rid of FunctionCx mark naked functions as `InstantiatedMode::GloballyShared` this makes sure that the function prototype is defined correctly, and we don't see LLVM complaining about a global value with invalid linkage monomorphize type given to `SymFn` remove hack that always emits `.globl` monomorphize type given to `Const` remove `linkage_directive` make naked functions always have external linkage mark naked functions as `#[inline(never)]` add test file for functional generics/const/impl/trait usage of naked functions --- compiler/rustc_codegen_llvm/src/attributes.rs | 14 +- compiler/rustc_codegen_llvm/src/mono_item.rs | 10 +- .../rustc_codegen_ssa/src/codegen_attrs.rs | 20 +- compiler/rustc_codegen_ssa/src/mir/mod.rs | 25 +-- .../rustc_codegen_ssa/src/mir/naked_asm.rs | 200 ++++++++++-------- compiler/rustc_codegen_ssa/src/mono_item.rs | 12 +- compiler/rustc_middle/src/mir/mono.rs | 8 +- tests/codegen/naked-fn/generics.rs | 117 ++++++++++ tests/codegen/naked-fn/instruction-set.rs | 43 ++++ tests/codegen/naked-fn/naked-functions.rs | 158 +++++++++++--- tests/codegen/naked-fn/naked-nocoverage.rs | 19 -- tests/crashes/124375.rs | 11 - .../ui/asm/naked-functions-instruction-set.rs | 2 +- tests/ui/asm/naked-functions.rs | 6 + 14 files changed, 443 insertions(+), 202 deletions(-) create mode 100644 tests/codegen/naked-fn/generics.rs create mode 100644 tests/codegen/naked-fn/instruction-set.rs delete mode 100644 tests/codegen/naked-fn/naked-nocoverage.rs delete mode 100644 tests/crashes/124375.rs diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index cb958c1d4d771..5552a2410601d 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -395,17 +395,9 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( to_add.push(MemoryEffects::None.create_attr(cx.llcx)); } if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { - to_add.push(AttributeKind::Naked.create_attr(cx.llcx)); - // HACK(jubilee): "indirect branch tracking" works by attaching prologues to functions. - // And it is a module-level attribute, so the alternative is pulling naked functions into - // new LLVM modules. Otherwise LLVM's "naked" functions come with endbr prefixes per - // https://github.com/rust-lang/rust/issues/98768 - to_add.push(AttributeKind::NoCfCheck.create_attr(cx.llcx)); - if llvm_util::get_version() < (19, 0, 0) { - // Prior to LLVM 19, branch-target-enforcement was disabled by setting the attribute to - // the string "false". Now it is disabled by absence of the attribute. - to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false")); - } + // do nothing; a naked function is converted into an extern function + // and a global assembly block. LLVM's support for naked functions is + // not used. } else { // Do not set sanitizer attributes for naked functions. to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize)); diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 33789c6261f10..4d1704f2c998f 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -2,6 +2,7 @@ use rustc_codegen_ssa::traits::*; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::bug; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::mono::{Linkage, Visibility}; use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance, TypeVisitableExt}; @@ -58,8 +59,15 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance)); - llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage)); let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); + let llvm_linkage = + if attrs.flags.contains(CodegenFnAttrFlags::NAKED) && linkage == Linkage::Internal { + // this is effectively an extern fn, and must have external linkage + llvm::Linkage::ExternalLinkage + } else { + base::linkage_to_llvm(linkage) + }; + llvm::set_linkage(lldecl, llvm_linkage); base::set_link_section(lldecl, attrs); if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR) && self.tcx.sess.target.supports_comdat() diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 0768b01cb15e5..01dd75c5ee85e 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -113,10 +113,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { sym::rustc_allocator_zeroed => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED } - sym::naked => { - // this attribute is ignored during codegen, because a function marked as naked is - // turned into a global asm block. - } + sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, sym::no_mangle => { if tcx.opt_item_name(did.to_def_id()).is_some() { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE @@ -545,6 +542,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } }); + // naked function MUST NOT be inlined! This attribute is required for the rust compiler itself, + // but not for the code generation backend because at that point the naked function will just be + // a declaration, with a definition provided in global assembly. + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { + codegen_fn_attrs.inline = InlineAttr::Never; + } + codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| { if !attr.has_name(sym::optimize) { return ia; @@ -629,14 +633,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { - // naked functions are generated using an extern function and global assembly. To - // make sure that these can be linked together by LLVM, the linkage should be external, - // unless the user explicitly configured something else (in which case any linker errors - // they encounter are their responsibility). - codegen_fn_attrs.linkage = codegen_fn_attrs.linkage.or(Some(Linkage::External)); - } - // Weak lang items have the same semantics as "std internal" symbols in the // sense that they're preserved through all our LTO passes and only // strippable by the linker. diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index b14e43acb6078..62f69af3f2f75 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -177,29 +177,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); debug!("fn_abi: {:?}", fn_abi); - if cx.tcx().has_attr(instance.def.def_id(), rustc_span::sym::naked) { - let cached_llbbs = IndexVec::new(); - - let fx: FunctionCx<'_, '_, Bx> = FunctionCx { - instance, - mir, - llfn, - fn_abi, - cx, - personality_slot: None, - cached_llbbs, - unreachable_block: None, - terminate_block: None, - cleanup_kinds: None, - landing_pads: IndexVec::from_elem(None, &mir.basic_blocks), - funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()), - locals: locals::Locals::empty(), - debug_context: None, - per_local_var_debug_info: None, - caller_location: None, - }; - - fx.codegen_naked_asm(instance); + if cx.tcx().codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) { + crate::mir::naked_asm::codegen_naked_asm::(cx, &mir, instance); return; } diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 1f41a9aefeb73..b4eb6110da8ca 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,74 +1,99 @@ -use crate::common; -use crate::mir::FunctionCx; -use crate::traits::{AsmMethods, BuilderMethods, GlobalAsmOperandRef}; -use rustc_middle::bug; -use rustc_middle::mir::InlineAsmOperand; -use rustc_middle::ty; -use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_attr::InstructionSetAttr; +use rustc_middle::mir::mono::{MonoItem, MonoItemData, Visibility}; +use rustc_middle::mir::{Body, InlineAsmOperand}; +use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf}; use rustc_middle::ty::{Instance, TyCtxt}; - -use rustc_span::sym; +use rustc_middle::{bug, ty}; use rustc_target::asm::InlineAsmArch; -impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { - pub fn codegen_naked_asm(&self, instance: Instance<'tcx>) { - let cx = &self.cx; - - let rustc_middle::mir::TerminatorKind::InlineAsm { - asm_macro: _, - template, - ref operands, - options, - line_spans, - targets: _, - unwind: _, - } = self.mir.basic_blocks.iter().next().unwrap().terminator().kind - else { - bug!("#[naked] functions should always terminate with an asm! block") - }; - - let operands: Vec<_> = - operands.iter().map(|op| self.inline_to_global_operand(op)).collect(); - - let (begin, end) = crate::mir::naked_asm::prefix_and_suffix(cx.tcx(), instance); - - let mut template_vec = Vec::new(); - template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin)); - template_vec.extend(template.iter().cloned()); - template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(end)); - - cx.codegen_global_asm(&template_vec, &operands, options, line_spans); - } +use crate::common; +use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods}; + +pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + cx: &'a Bx::CodegenCx, + mir: &Body<'tcx>, + instance: Instance<'tcx>, +) { + let rustc_middle::mir::TerminatorKind::InlineAsm { + asm_macro: _, + template, + ref operands, + options, + line_spans, + targets: _, + unwind: _, + } = mir.basic_blocks.iter().next().unwrap().terminator().kind + else { + bug!("#[naked] functions should always terminate with an asm! block") + }; - fn inline_to_global_operand(&self, op: &InlineAsmOperand<'tcx>) -> GlobalAsmOperandRef<'tcx> { - match op { - InlineAsmOperand::Const { value } => { - let const_value = self.eval_mir_constant(value); - let string = common::asm_const_to_str( - self.cx.tcx(), - value.span, - const_value, - self.cx.layout_of(value.ty()), - ); - GlobalAsmOperandRef::Const { string } - } - InlineAsmOperand::SymFn { value } => { - let instance = match value.ty().kind() { - &ty::FnDef(def_id, args) => Instance::new(def_id, args), - _ => bug!("asm sym is not a function"), - }; + let operands: Vec<_> = + operands.iter().map(|op| inline_to_global_operand::(cx, instance, op)).collect(); - GlobalAsmOperandRef::SymFn { instance } - } - InlineAsmOperand::SymStatic { def_id } => { - GlobalAsmOperandRef::SymStatic { def_id: *def_id } - } - InlineAsmOperand::In { .. } - | InlineAsmOperand::Out { .. } - | InlineAsmOperand::InOut { .. } - | InlineAsmOperand::Label { .. } => { - bug!("invalid operand type for naked_asm!") - } + let item_data = cx.codegen_unit().items().get(&MonoItem::Fn(instance)).unwrap(); + let (begin, end) = crate::mir::naked_asm::prefix_and_suffix(cx.tcx(), instance, item_data); + + let mut template_vec = Vec::new(); + template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin.into())); + template_vec.extend(template.iter().cloned()); + template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(end.into())); + + cx.codegen_global_asm(&template_vec, &operands, options, line_spans); +} + +fn inline_to_global_operand<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + cx: &'a Bx::CodegenCx, + instance: Instance<'tcx>, + op: &InlineAsmOperand<'tcx>, +) -> GlobalAsmOperandRef<'tcx> { + match op { + InlineAsmOperand::Const { value } => { + let const_value = instance + .instantiate_mir_and_normalize_erasing_regions( + cx.tcx(), + cx.typing_env(), + ty::EarlyBinder::bind(value.const_), + ) + .eval(cx.tcx(), cx.typing_env(), value.span) + .expect("erroneous constant missed by mono item collection"); + + let mono_type = instance.instantiate_mir_and_normalize_erasing_regions( + cx.tcx(), + cx.typing_env(), + ty::EarlyBinder::bind(value.ty()), + ); + + let string = common::asm_const_to_str( + cx.tcx(), + value.span, + const_value, + cx.layout_of(mono_type), + ); + + GlobalAsmOperandRef::Const { string } + } + InlineAsmOperand::SymFn { value } => { + let mono_type = instance.instantiate_mir_and_normalize_erasing_regions( + cx.tcx(), + cx.typing_env(), + ty::EarlyBinder::bind(value.ty()), + ); + + let instance = match mono_type.kind() { + &ty::FnDef(def_id, args) => Instance::new(def_id, args), + _ => bug!("asm sym is not a function"), + }; + + GlobalAsmOperandRef::SymFn { instance } + } + InlineAsmOperand::SymStatic { def_id } => { + GlobalAsmOperandRef::SymStatic { def_id: *def_id } + } + InlineAsmOperand::In { .. } + | InlineAsmOperand::Out { .. } + | InlineAsmOperand::InOut { .. } + | InlineAsmOperand::Label { .. } => { + bug!("invalid operand type for naked_asm!") } } } @@ -91,7 +116,11 @@ impl AsmBinaryFormat { } } -fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (String, String) { +fn prefix_and_suffix<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + item_data: &MonoItemData, +) -> (String, String) { use std::fmt::Write; let target = &tcx.sess.target; @@ -105,24 +134,18 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri let asm_name = format!("{}{}", if mangle { "_" } else { "" }, tcx.symbol_name(instance).name); - let opt_section = tcx - .get_attr(instance.def.def_id(), sym::link_section) - .and_then(|attr| attr.value_str()) - .map(|attr| attr.as_str().to_string()); - - let instruction_set = - tcx.get_attr(instance.def.def_id(), sym::instruction_set).and_then(|attr| attr.value_str()); + let attrs = tcx.codegen_fn_attrs(instance.def_id()); + let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string()); let (arch_prefix, arch_suffix) = if is_arm { ( - match instruction_set { + match attrs.instruction_set { None => match is_thumb { true => ".thumb\n.thumb_func", false => ".arm", }, - Some(sym::a32) => ".arm", - Some(sym::t32) => ".thumb\n.thumb_func", - Some(other) => bug!("invalid instruction set: {other}"), + Some(InstructionSetAttr::ArmA32) => ".arm", + Some(InstructionSetAttr::ArmT32) => ".thumb\n.thumb_func", }, match is_thumb { true => ".thumb", @@ -137,7 +160,7 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri let mut end = String::new(); match AsmBinaryFormat::from_target(&tcx.sess.target) { AsmBinaryFormat::Elf => { - let section = opt_section.unwrap_or(format!(".text.{asm_name}")); + let section = link_section.unwrap_or(format!(".text.{asm_name}")); let progbits = match is_arm { true => "%progbits", @@ -152,11 +175,10 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap(); writeln!(begin, ".balign 4").unwrap(); writeln!(begin, ".globl {asm_name}").unwrap(); - writeln!(begin, ".hidden {asm_name}").unwrap(); - writeln!(begin, ".type {asm_name}, {function}").unwrap(); - if let Some(instruction_set) = instruction_set { - writeln!(begin, "{}", instruction_set.as_str()).unwrap(); + if let Visibility::Hidden = item_data.visibility { + writeln!(begin, ".hidden {asm_name}").unwrap(); } + writeln!(begin, ".type {asm_name}, {function}").unwrap(); if !arch_prefix.is_empty() { writeln!(begin, "{}", arch_prefix).unwrap(); } @@ -170,13 +192,12 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri } } AsmBinaryFormat::Macho => { - let section = opt_section.unwrap_or("__TEXT,__text".to_string()); + let section = link_section.unwrap_or("__TEXT,__text".to_string()); writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap(); writeln!(begin, ".balign 4").unwrap(); writeln!(begin, ".globl {asm_name}").unwrap(); - writeln!(begin, ".private_extern {asm_name}").unwrap(); - if let Some(instruction_set) = instruction_set { - writeln!(begin, "{}", instruction_set.as_str()).unwrap(); + if let Visibility::Hidden = item_data.visibility { + writeln!(begin, ".private_extern {asm_name}").unwrap(); } writeln!(begin, "{asm_name}:").unwrap(); @@ -187,7 +208,7 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri } } AsmBinaryFormat::Coff => { - let section = opt_section.unwrap_or(format!(".text.{asm_name}")); + let section = link_section.unwrap_or(format!(".text.{asm_name}")); writeln!(begin, ".pushsection {},\"xr\"", section).unwrap(); writeln!(begin, ".balign 4").unwrap(); writeln!(begin, ".globl {asm_name}").unwrap(); @@ -195,9 +216,6 @@ fn prefix_and_suffix<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> (Stri writeln!(begin, ".scl 2").unwrap(); writeln!(begin, ".type 32").unwrap(); writeln!(begin, ".endef {asm_name}").unwrap(); - if let Some(instruction_set) = instruction_set { - writeln!(begin, "{}", instruction_set.as_str()).unwrap(); - } writeln!(begin, "{asm_name}:").unwrap(); writeln!(end).unwrap(); diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 04159422e3cb2..085ef1cd8ea36 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -1,8 +1,9 @@ use rustc_hir as hir; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::mir::mono::{LinkageInfo, MonoItem, Visibility}; -use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::ty::Instance; +use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::{span_bug, ty}; use tracing::debug; @@ -135,7 +136,14 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { cx.predefine_static(def_id, linkage_info.into_linkage(), visibility, symbol_name); } MonoItem::Fn(instance) => { - cx.predefine_fn(instance, linkage_info.into_linkage(), visibility, symbol_name); + let attrs = cx.tcx().codegen_fn_attrs(instance.def_id()); + let linkage = if attrs.flags.contains(CodegenFnAttrFlags::NAKED) { + linkage_info.into_naked_linkage() + } else { + linkage_info.into_linkage() + }; + + cx.predefine_fn(instance, linkage, visibility, symbol_name); } MonoItem::GlobalAsm(..) => {} } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index ca230d7fa8584..4287b2c574ffe 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -19,6 +19,7 @@ use rustc_target::spec::SymbolVisibility; use tracing::debug; use crate::dep_graph::{DepNode, WorkProduct, WorkProductId}; +use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::ty::{GenericArgs, Instance, InstanceKind, SymbolName, TyCtxt}; /// Describes how a monomorphization will be instantiated in object files. @@ -104,7 +105,9 @@ impl<'tcx> MonoItem<'tcx> { let entry_def_id = tcx.entry_fn(()).map(|(id, _)| id); // If this function isn't inlined or otherwise has an extern // indicator, then we'll be creating a globally shared version. - if tcx.codegen_fn_attrs(instance.def_id()).contains_extern_indicator() + let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id()); + if codegen_fn_attrs.contains_extern_indicator() + || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) || !instance.def.generates_cgu_internal_copy(tcx) || Some(instance.def_id()) == entry_def_id { @@ -302,6 +305,9 @@ impl LinkageInfo { /// Naked functions are generated as a separate declaration (effectively an extern fn) and /// definition (using global assembly). To link them together, some flavor of external linkage /// must be used. + /// + /// This should be just an implementation detail of the backend, which is why this translation + /// is made at the final moment. pub const fn into_naked_linkage(self) -> Linkage { match self { // promote Weak linkage to ExternalWeak diff --git a/tests/codegen/naked-fn/generics.rs b/tests/codegen/naked-fn/generics.rs new file mode 100644 index 0000000000000..5bb40cddea56c --- /dev/null +++ b/tests/codegen/naked-fn/generics.rs @@ -0,0 +1,117 @@ +//@ compile-flags: -O +//@ only-x86_64 + +#![crate_type = "lib"] +#![feature(naked_functions, asm_const)] + +use std::arch::asm; + +#[no_mangle] +fn test(x: u64) { + // just making sure these symbols get used + using_const_generics::<1>(x); + using_const_generics::<2>(x); + + generic_function::(x as i64); + + let foo = Foo(x); + + foo.method(); + foo.trait_method(); +} + +// CHECK: .balign 4 +// CHECK: add rax, 2 +// CHECK: add rax, 42 + +// CHECK: .balign 4 +// CHECK: add rax, 1 +// CHECK: add rax, 42 + +#[naked] +pub extern "C" fn using_const_generics(x: u64) -> u64 { + const M: u64 = 42; + + unsafe { + asm!( + "xor rax, rax", + "add rax, rdi", + "add rax, {}", + "add rax, {}", + "ret", + const N, + const M, + options(noreturn), + ) + } +} + +trait Invert { + fn invert(self) -> Self; +} + +impl Invert for i64 { + fn invert(self) -> Self { + -1 * self + } +} + +// CHECK-LABEL: generic_function +// CHECK: .balign 4 +// CHECK: call +// CHECK: ret + +#[naked] +pub extern "C" fn generic_function(x: i64) -> i64 { + unsafe { + asm!( + "call {}", + "ret", + sym ::invert, + options(noreturn), + ) + } +} + +#[derive(Copy, Clone)] +#[repr(transparent)] +struct Foo(u64); + +// CHECK-LABEL: method +// CHECK: .balign 4 +// CHECK: mov rax, rdi + +impl Foo { + #[naked] + #[no_mangle] + extern "C" fn method(self) -> u64 { + unsafe { asm!("mov rax, rdi", "ret", options(noreturn)) } + } +} + +// CHECK-LABEL: trait_method +// CHECK: .balign 4 +// CHECK: mov rax, rdi + +trait Bar { + extern "C" fn trait_method(self) -> u64; +} + +impl Bar for Foo { + #[naked] + #[no_mangle] + extern "C" fn trait_method(self) -> u64 { + unsafe { asm!("mov rax, rdi", "ret", options(noreturn)) } + } +} + +// CHECK-LABEL: naked_with_args_and_return +// CHECK: .balign 4 +// CHECK: lea rax, [rdi + rsi] + +// this previously ICE'd, see https://github.com/rust-lang/rust/issues/124375 +#[naked] +#[no_mangle] +pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { + asm!("lea rax, [rdi + rsi]", "ret", options(noreturn)); +} diff --git a/tests/codegen/naked-fn/instruction-set.rs b/tests/codegen/naked-fn/instruction-set.rs new file mode 100644 index 0000000000000..509c24f9457cf --- /dev/null +++ b/tests/codegen/naked-fn/instruction-set.rs @@ -0,0 +1,43 @@ +//@ compile-flags: --target armv5te-none-eabi +//@ needs-llvm-components: arm + +#![crate_type = "lib"] +#![feature(no_core, lang_items, rustc_attrs, naked_functions)] +#![no_core] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +// CHECK-LABEL: test_unspecified: +// CHECK: .arm +#[no_mangle] +#[naked] +unsafe extern "C" fn test_unspecified() { + asm!("bx lr", options(noreturn)); +} + +// CHECK-LABEL: test_thumb: +// CHECK: .thumb +// CHECK: .thumb_func +#[no_mangle] +#[naked] +#[instruction_set(arm::t32)] +unsafe extern "C" fn test_thumb() { + asm!("bx lr", options(noreturn)); +} + +// CHECK-LABEL: test_arm: +// CHECK: .arm +#[no_mangle] +#[naked] +#[instruction_set(arm::t32)] +unsafe extern "C" fn test_arm() { + asm!("bx lr", options(noreturn)); +} diff --git a/tests/codegen/naked-fn/naked-functions.rs b/tests/codegen/naked-fn/naked-functions.rs index 00d64089ad8bc..8b12693da8f66 100644 --- a/tests/codegen/naked-fn/naked-functions.rs +++ b/tests/codegen/naked-fn/naked-functions.rs @@ -1,49 +1,147 @@ -//@ compile-flags: -C no-prepopulate-passes -Copt-level=0 -//@ needs-asm-support -//@ only-x86_64 +//@ revisions: linux windows macos thumb +// +//@[linux] compile-flags: --target x86_64-unknown-linux-gnu +//@[linux] needs-llvm-components: x86 +//@[windows] compile-flags: --target x86_64-pc-windows-gnu +//@[windows] needs-llvm-components: x86 +//@[macos] compile-flags: --target aarch64-apple-darwin +//@[macos] needs-llvm-components: arm +//@[thumb] compile-flags: --target thumbv7em-none-eabi +//@[thumb] needs-llvm-components: arm #![crate_type = "lib"] -#![feature(naked_functions)] -use std::arch::naked_asm; - -// CHECK: module asm ".intel_syntax" -// CHECK: .pushsection .text.naked_empty,\22ax\22, @progbits -// CHECK: .balign 4" -// CHECK: .globl naked_empty" -// CHECK: .hidden naked_empty" -// CHECK: .type naked_empty, @function" +#![feature(no_core, lang_items, rustc_attrs, naked_functions)] +#![no_core] + +#[rustc_builtin_macro] +macro_rules! naked_asm { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +// linux,windows: .intel_syntax +// +// linux: .pushsection .text.naked_empty,\22ax\22, @progbits +// macos: .pushsection __TEXT,__text,regular,pure_instructions +// windows: .pushsection .text.naked_empty,\22xr\22 +// thumb: .pushsection .text.naked_empty,\22ax\22, %progbits +// +// CHECK: .balign 4 +// +// linux,windows,thumb: .globl naked_empty +// macos: .globl _naked_empty +// +// CHECK-NOT: .private_extern +// CHECK-NOT: .hidden +// +// linux: .type naked_empty, @function +// +// windows: .def naked_empty +// windows: .scl 2 +// windows: .type 32 +// windows: .endef naked_empty +// +// thumb: .type naked_empty, %function +// thumb: .thumb +// thumb: .thumb_func +// // CHECK-LABEL: naked_empty: -// CHECK: ret +// +// linux,macos,windows: ret +// thumb: bx lr +// // CHECK: .popsection -// CHECK: .att_syntax +// +// thumb: .thumb +// +// linux,windows: .att_syntax #[no_mangle] #[naked] pub unsafe extern "C" fn naked_empty() { - // CHECK-NEXT: {{.+}}: - // CHECK-NEXT: call void asm - // CHECK-NEXT: unreachable + #[cfg(not(all(target_arch = "arm", target_feature = "thumb-mode")))] naked_asm!("ret"); + + #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] + naked_asm!("bx lr"); } -// CHECK: .intel_syntax -// CHECK: .pushsection .text.naked_with_args_and_return,\22ax\22, @progbits +// linux,windows: .intel_syntax +// +// linux: .pushsection .text.naked_with_args_and_return,\22ax\22, @progbits +// macos: .pushsection __TEXT,__text,regular,pure_instructions +// windows: .pushsection .text.naked_with_args_and_return,\22xr\22 +// thumb: .pushsection .text.naked_with_args_and_return,\22ax\22, %progbits +// // CHECK: .balign 4 -// CHECK: .globl naked_with_args_and_return -// CHECK: .hidden naked_with_args_and_return -// CHECK: .type naked_with_args_and_return, @function +// +// linux,windows,thumb: .globl naked_with_args_and_return +// macos: .globl _naked_with_args_and_return +// +// CHECK-NOT: .private_extern +// CHECK-NOT: .hidden +// +// linux: .type naked_with_args_and_return, @function +// +// windows: .def naked_with_args_and_return +// windows: .scl 2 +// windows: .type 32 +// windows: .endef naked_with_args_and_return +// +// thumb: .type naked_with_args_and_return, %function +// thumb: .thumb +// thumb: .thumb_func +// // CHECK-LABEL: naked_with_args_and_return: -// CHECK: lea rax, [rdi + rsi] -// CHECK: ret -// CHECK: .size naked_with_args_and_return, . - naked_with_args_and_return +// +// linux, windows: lea rax, [rdi + rsi] +// macos: add x0, x0, x1 +// thumb: adds r0, r0, r1 +// +// linux,macos,windows: ret +// thumb: bx lr +// // CHECK: .popsection -// CHECK: .att_syntax +// +// thumb: .thumb +// +// linux,windows: .att_syntax #[no_mangle] #[naked] pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { - // CHECK-NEXT: {{.+}}: - // CHECK-NEXT: call void asm - // CHECK-NEXT: unreachable - naked_asm!("lea rax, [rdi + rsi]", "ret"); + #[cfg(any(target_os = "windows", target_os = "linux"))] + { + naked_asm!("lea rax, [rdi + rsi]", "ret") + } + + #[cfg(target_os = "macos")] + { + naked_asm!("add x0, x0, x1", "ret") + } + + #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] + { + naked_asm!("adds r0, r0, r1", "bx lr") + } +} + +// linux: .pushsection .text.some_different_name,\22ax\22, @progbits +// macos: .pushsection .text.some_different_name,regular,pure_instructions +// windows: .pushsection .text.some_different_name,\22xr\22 +// thumb: .pushsection .text.some_different_name,\22ax\22, %progbits +// CHECK-LABEL: test_link_section: +#[no_mangle] +#[naked] +#[link_section = ".text.some_different_name"] +pub unsafe extern "C" fn test_link_section() { + #[cfg(not(all(target_arch = "arm", target_feature = "thumb-mode")))] + naked_asm!("ret", options(noreturn)); + + #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] + naked_asm!("bx lr", options(noreturn)); } diff --git a/tests/codegen/naked-fn/naked-nocoverage.rs b/tests/codegen/naked-fn/naked-nocoverage.rs deleted file mode 100644 index f63661bcd3a7a..0000000000000 --- a/tests/codegen/naked-fn/naked-nocoverage.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Checks that naked functions are not instrumented by -Cinstrument-coverage. -// Regression test for issue #105170. -// -//@ needs-asm-support -//@ compile-flags: -Zno-profiler-runtime -//@ compile-flags: -Cinstrument-coverage -#![crate_type = "lib"] -#![feature(naked_functions)] -use std::arch::naked_asm; - -#[naked] -#[no_mangle] -pub unsafe extern "C" fn f() { - // CHECK: define {{(dso_local )?}}void @f() - // CHECK-NEXT: start: - // CHECK-NEXT: call void asm - // CHECK-NEXT: unreachable - naked_asm!(""); -} diff --git a/tests/crashes/124375.rs b/tests/crashes/124375.rs deleted file mode 100644 index 1d877caeb8bc1..0000000000000 --- a/tests/crashes/124375.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ known-bug: #124375 -//@ compile-flags: -Zmir-opt-level=0 -//@ only-x86_64 -#![crate_type = "lib"] -#![feature(naked_functions)] -use std::arch::naked_asm; - -#[naked] -pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { - naked_asm!("lea rax, [rdi + rsi]", "ret"); -} diff --git a/tests/ui/asm/naked-functions-instruction-set.rs b/tests/ui/asm/naked-functions-instruction-set.rs index 37c7b52c191cd..3a6e7a46ce5a7 100644 --- a/tests/ui/asm/naked-functions-instruction-set.rs +++ b/tests/ui/asm/naked-functions-instruction-set.rs @@ -24,7 +24,7 @@ unsafe extern "C" fn test_thumb() { #[no_mangle] #[naked] -#[instruction_set(arm::t32)] +#[instruction_set(arm::a32)] unsafe extern "C" fn test_arm() { naked_asm!("bx lr"); } diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs index 5c58f1498cc97..0939b5b57e5e1 100644 --- a/tests/ui/asm/naked-functions.rs +++ b/tests/ui/asm/naked-functions.rs @@ -183,6 +183,12 @@ pub unsafe extern "C" fn invalid_asm_syntax(a: u32) -> u32 { //~^ ERROR asm template must be a string literal } +// this previously ICE'd, see https://github.com/rust-lang/rust/issues/124375 +#[naked] +pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { + asm!("lea rax, [rdi + rsi]", "ret", options(noreturn)); +} + #[cfg(target_arch = "x86_64")] #[cfg_attr(target_pointer_width = "64", no_mangle)] #[naked] From 23d9741be3bd6471689327ee4e58cd5d1967b760 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 Nov 2024 09:52:20 +0100 Subject: [PATCH 10/24] move slice::swap_unchecked constness to slice_swap_unchecked feature gate --- library/core/src/slice/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index a24417dba8cc2..ec04852d44b44 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -959,7 +959,7 @@ impl [T] { /// [`swap`]: slice::swap /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html #[unstable(feature = "slice_swap_unchecked", issue = "88539")] - #[rustc_const_unstable(feature = "const_swap", issue = "83163")] + #[rustc_const_unstable(feature = "slice_swap_unchecked", issue = "88539")] pub const unsafe fn swap_unchecked(&mut self, a: usize, b: usize) { assert_unsafe_precondition!( check_library_ub, From ede5f0111d21b52f3f821455512d5c5b13fe9e29 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 Nov 2024 09:57:19 +0100 Subject: [PATCH 11/24] move swap_nonoverlapping constness to separate feature gate --- library/core/src/ptr/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 6147e9f5e6190..b6fc0caebd020 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1009,6 +1009,7 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_swap", issue = "83163")] #[rustc_diagnostic_item = "ptr_swap"] +#[rustc_const_stable_indirect] pub const unsafe fn swap(x: *mut T, y: *mut T) { // Give ourselves some scratch space to work with. // We do not have to worry about drops: `MaybeUninit` does nothing when dropped. @@ -1069,7 +1070,7 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { /// ``` #[inline] #[stable(feature = "swap_nonoverlapping", since = "1.27.0")] -#[rustc_const_unstable(feature = "const_swap", issue = "83163")] +#[rustc_const_unstable(feature = "const_swap_nonoverlapping", issue = "133668")] #[rustc_diagnostic_item = "ptr_swap_nonoverlapping"] pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { #[allow(unused)] @@ -1129,7 +1130,6 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { /// LLVM can vectorize this (at least it can for the power-of-two-sized types /// `swap_nonoverlapping` tries to use) so no need to manually SIMD it. #[inline] -#[rustc_const_unstable(feature = "const_swap", issue = "83163")] const unsafe fn swap_nonoverlapping_simple_untyped(x: *mut T, y: *mut T, count: usize) { let x = x.cast::>(); let y = y.cast::>(); From 2a05e5be4f09503e0d33123bbf2b4343872d1162 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 Nov 2024 10:04:41 +0100 Subject: [PATCH 12/24] add test for bytewise ptr::swap of a pointer --- library/core/tests/lib.rs | 1 + library/core/tests/ptr.rs | 19 +++++++++++++++++++ tests/ui/consts/missing_span_in_backtrace.rs | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 40129619ce50e..29de66852a426 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -18,6 +18,7 @@ #![feature(const_eval_select)] #![feature(const_heap)] #![feature(const_nonnull_new)] +#![feature(const_swap)] #![feature(const_trait_impl)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 91f8c977d088a..7e9773f2fb952 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -897,6 +897,25 @@ fn test_const_copy() { }; } +#[test] +fn test_const_swap() { + const { + let mut ptr1 = &1; + let mut ptr2 = &666; + + // Swap ptr1 and ptr2, bytewise. `swap` does not take a count + // so the best we can do is use an array. + type T = [u8; mem::size_of::<&i32>()]; + unsafe { + ptr::swap(ptr::from_mut(&mut ptr1).cast::(), ptr::from_mut(&mut ptr2).cast::()); + } + + // Make sure they still work. + assert!(*ptr1 == 666); + assert!(*ptr2 == 1); + }; +} + #[test] fn test_null_array_as_slice() { let arr: *mut [u8; 4] = null_mut(); diff --git a/tests/ui/consts/missing_span_in_backtrace.rs b/tests/ui/consts/missing_span_in_backtrace.rs index ea457c96f153c..703cc7fbf89bc 100644 --- a/tests/ui/consts/missing_span_in_backtrace.rs +++ b/tests/ui/consts/missing_span_in_backtrace.rs @@ -1,7 +1,7 @@ //@ compile-flags: -Z ui-testing=no -#![feature(const_swap)] +#![feature(const_swap_nonoverlapping)] use std::{ mem::{self, MaybeUninit}, ptr, From 9836196e3cca6661e752c3acd8bc207f181736ed Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 30 Nov 2024 01:59:17 -0800 Subject: [PATCH 13/24] Fix chaining `carrying_add`s Something about the MIR lowering for `||` ended up breaking this, but it's fixed by changing the code to use `|` instead. I also added an assembly test to ensure it *keeps* being `adc`. --- library/core/src/num/uint_macros.rs | 2 +- tests/assembly/x86_64-bigint-add.rs | 33 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 tests/assembly/x86_64-bigint-add.rs diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 90b986f4998db..0ebd765b54900 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2354,7 +2354,7 @@ macro_rules! uint_impl { // to generate optimal code for now, and LLVM doesn't have an equivalent intrinsic let (a, b) = self.overflowing_add(rhs); let (c, d) = a.overflowing_add(carry as $SelfT); - (c, b || d) + (c, b | d) } /// Calculates `self` + `rhs` with a signed `rhs`. diff --git a/tests/assembly/x86_64-bigint-add.rs b/tests/assembly/x86_64-bigint-add.rs new file mode 100644 index 0000000000000..4bcb9732c6405 --- /dev/null +++ b/tests/assembly/x86_64-bigint-add.rs @@ -0,0 +1,33 @@ +//@ only-x86_64 +//@ assembly-output: emit-asm +//@ compile-flags: --crate-type=lib -O -C target-cpu=x86-64-v4 +//@ compile-flags: -C llvm-args=-x86-asm-syntax=intel + +#![no_std] +#![feature(bigint_helper_methods)] + +// This checks that the `carrying_add` implementation successfully chains, to catch +// issues like + +// This forces the ABI to avoid the windows-vs-linux ABI differences. + +// CHECK-LABEL: bigint_chain_carrying_add: +#[no_mangle] +pub unsafe extern "sysv64" fn bigint_chain_carrying_add( + dest: *mut u64, + src1: *const u64, + src2: *const u64, + n: usize, + mut carry: bool, +) -> bool { + // CHECK: mov [[TEMP:r..]], qword ptr [rsi + 8*[[IND:r..]] + 8] + // CHECK: adc [[TEMP]], qword ptr [rdx + 8*[[IND]] + 8] + // CHECK: mov qword ptr [rdi + 8*[[IND]] + 8], [[TEMP]] + // CHECK: mov [[TEMP]], qword ptr [rsi + 8*[[IND]] + 16] + // CHECK: adc [[TEMP]], qword ptr [rdx + 8*[[IND]] + 16] + // CHECK: mov qword ptr [rdi + 8*[[IND]] + 16], [[TEMP]] + for i in 0..n { + (*dest.add(i), carry) = u64::carrying_add(*src1.add(i), *src2.add(i), carry); + } + carry +} From 589ebb815d5e078de200155f207ab7fcc550cf9c Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 26 Jul 2024 16:54:15 +0200 Subject: [PATCH 14/24] make naked functions always have external linkage *in LLVM*. If we do it earlier, then some other logic causes invalid visibility for the item (exporting when it shouldn't). --- compiler/rustc_codegen_llvm/src/mono_item.rs | 10 +--------- tests/ui/asm/naked-functions.rs | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 4d1704f2c998f..b91941396ed81 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -2,7 +2,6 @@ use rustc_codegen_ssa::traits::*; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::bug; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::mono::{Linkage, Visibility}; use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance, TypeVisitableExt}; @@ -60,14 +59,7 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance)); let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); - let llvm_linkage = - if attrs.flags.contains(CodegenFnAttrFlags::NAKED) && linkage == Linkage::Internal { - // this is effectively an extern fn, and must have external linkage - llvm::Linkage::ExternalLinkage - } else { - base::linkage_to_llvm(linkage) - }; - llvm::set_linkage(lldecl, llvm_linkage); + llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage)); base::set_link_section(lldecl, attrs); if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR) && self.tcx.sess.target.supports_comdat() diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs index 0939b5b57e5e1..586645edac969 100644 --- a/tests/ui/asm/naked-functions.rs +++ b/tests/ui/asm/naked-functions.rs @@ -186,7 +186,7 @@ pub unsafe extern "C" fn invalid_asm_syntax(a: u32) -> u32 { // this previously ICE'd, see https://github.com/rust-lang/rust/issues/124375 #[naked] pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { - asm!("lea rax, [rdi + rsi]", "ret", options(noreturn)); + naked_asm!("lea rax, [rdi + rsi]", "ret"); } #[cfg(target_arch = "x86_64")] From 2e24cdbf30ca0ae5473cf0aa06140e044b805f1a Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 11 Aug 2024 12:21:42 +0200 Subject: [PATCH 15/24] squashed changes: - codegen tests: change `windows` to `win` - cleanup - fix review comments - better way of checking for thumb - get the mangled name from the codegen backend - propagate function alignment - fix gcc backend - fix asan test - check that assembler mode restored --- compiler/rustc_codegen_gcc/src/asm.rs | 7 +++ compiler/rustc_codegen_llvm/src/asm.rs | 8 +++ .../rustc_codegen_ssa/src/mir/naked_asm.rs | 31 ++++++------ compiler/rustc_codegen_ssa/src/traits/asm.rs | 7 +++ tests/codegen/naked-asan.rs | 6 ++- tests/codegen/naked-fn/aligned.rs | 9 ++-- tests/codegen/naked-fn/generics.rs | 14 +++--- tests/codegen/naked-fn/instruction-set.rs | 36 +++++++++---- tests/codegen/naked-fn/naked-functions.rs | 50 +++++++++---------- 9 files changed, 101 insertions(+), 67 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 341d1b9c179b7..199d22e02e287 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -865,6 +865,13 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { template_str.push_str("\n.popsection"); self.context.add_top_level_asm(None, &template_str); } + + fn mangled_name(&self, instance: Instance<'tcx>) -> String { + // TODO(@Amanieu): Additional mangling is needed on + // some targets to add a leading underscore (Mach-O) + // or byte count suffixes (x86 Windows). + self.tcx.symbol_name(instance).name.to_string() + } } fn modifier_to_gcc( diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 9aa01bd1b956c..a6fa1fc3e19a4 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -442,6 +442,14 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { ); } } + + fn mangled_name(&self, instance: Instance<'tcx>) -> String { + let llval = self.get_fn(instance); + llvm::build_string(|s| unsafe { + llvm::LLVMRustGetMangledName(llval, s); + }) + .expect("symbol is not valid UTF-8") + } } pub(crate) fn inline_asm_call<'ll>( diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index b4eb6110da8ca..65470dd6d5371 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -4,7 +4,7 @@ use rustc_middle::mir::{Body, InlineAsmOperand}; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf}; use rustc_middle::ty::{Instance, TyCtxt}; use rustc_middle::{bug, ty}; -use rustc_target::asm::InlineAsmArch; +use rustc_span::sym; use crate::common; use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods}; @@ -31,7 +31,8 @@ pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( operands.iter().map(|op| inline_to_global_operand::(cx, instance, op)).collect(); let item_data = cx.codegen_unit().items().get(&MonoItem::Fn(instance)).unwrap(); - let (begin, end) = crate::mir::naked_asm::prefix_and_suffix(cx.tcx(), instance, item_data); + let name = cx.mangled_name(instance); + let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data); let mut template_vec = Vec::new(); template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin.into())); @@ -108,7 +109,7 @@ impl AsmBinaryFormat { fn from_target(target: &rustc_target::spec::Target) -> Self { if target.is_like_windows { Self::Coff - } else if target.options.vendor == "apple" { + } else if target.is_like_osx { Self::Macho } else { Self::Elf @@ -119,24 +120,20 @@ impl AsmBinaryFormat { fn prefix_and_suffix<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, + asm_name: &str, item_data: &MonoItemData, ) -> (String, String) { use std::fmt::Write; - let target = &tcx.sess.target; - let target_arch = tcx.sess.asm_arch; - - let is_arm = target.arch == "arm"; - let is_thumb = is_arm && target.llvm_target.contains("thumb"); - - let mangle = (target.is_like_windows && matches!(target_arch, Some(InlineAsmArch::X86))) - || target.options.vendor == "apple"; - - let asm_name = format!("{}{}", if mangle { "_" } else { "" }, tcx.symbol_name(instance).name); + let is_arm = tcx.sess.target.arch == "arm"; + let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode); let attrs = tcx.codegen_fn_attrs(instance.def_id()); let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string()); + let align = attrs.alignment.map(|a| a.bytes()).unwrap_or(4); + // See https://sourceware.org/binutils/docs/as/ARM-Directives.html for info on these directives. + // In particular, `.arm` can also be written `.code 32` and `.thumb` as `.code 16`. let (arch_prefix, arch_suffix) = if is_arm { ( match attrs.instruction_set { @@ -144,8 +141,8 @@ fn prefix_and_suffix<'tcx>( true => ".thumb\n.thumb_func", false => ".arm", }, - Some(InstructionSetAttr::ArmA32) => ".arm", Some(InstructionSetAttr::ArmT32) => ".thumb\n.thumb_func", + Some(InstructionSetAttr::ArmA32) => ".arm", }, match is_thumb { true => ".thumb", @@ -173,7 +170,7 @@ fn prefix_and_suffix<'tcx>( }; writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap(); - writeln!(begin, ".balign 4").unwrap(); + writeln!(begin, ".balign {align}").unwrap(); writeln!(begin, ".globl {asm_name}").unwrap(); if let Visibility::Hidden = item_data.visibility { writeln!(begin, ".hidden {asm_name}").unwrap(); @@ -194,7 +191,7 @@ fn prefix_and_suffix<'tcx>( AsmBinaryFormat::Macho => { let section = link_section.unwrap_or("__TEXT,__text".to_string()); writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap(); - writeln!(begin, ".balign 4").unwrap(); + writeln!(begin, ".balign {align}").unwrap(); writeln!(begin, ".globl {asm_name}").unwrap(); if let Visibility::Hidden = item_data.visibility { writeln!(begin, ".private_extern {asm_name}").unwrap(); @@ -210,7 +207,7 @@ fn prefix_and_suffix<'tcx>( AsmBinaryFormat::Coff => { let section = link_section.unwrap_or(format!(".text.{asm_name}")); writeln!(begin, ".pushsection {},\"xr\"", section).unwrap(); - writeln!(begin, ".balign 4").unwrap(); + writeln!(begin, ".balign {align}").unwrap(); writeln!(begin, ".globl {asm_name}").unwrap(); writeln!(begin, ".def {asm_name}").unwrap(); writeln!(begin, ".scl 2").unwrap(); diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index f4853da115648..7767bffbfbfd6 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -68,4 +68,11 @@ pub trait AsmCodegenMethods<'tcx> { options: InlineAsmOptions, line_spans: &[Span], ); + + /// The mangled name of this instance + /// + /// Additional mangling is used on + /// some targets to add a leading underscore (Mach-O) + /// or byte count suffixes (x86 Windows). + fn mangled_name(&self, instance: Instance<'tcx>) -> String; } diff --git a/tests/codegen/naked-asan.rs b/tests/codegen/naked-asan.rs index bcaa60baeffd8..639f069c0d9e5 100644 --- a/tests/codegen/naked-asan.rs +++ b/tests/codegen/naked-asan.rs @@ -8,7 +8,11 @@ #![no_std] #![feature(abi_x86_interrupt, naked_functions)] -// CHECK: define x86_intrcc void @page_fault_handler(ptr {{.*}}%0, i64 {{.*}}%1){{.*}}#[[ATTRS:[0-9]+]] { +pub fn caller() { + page_fault_handler(1, 2); +} + +// CHECK: declare x86_intrcc void @page_fault_handler(ptr {{.*}}, i64 {{.*}}){{.*}}#[[ATTRS:[0-9]+]] // CHECK-NOT: memcpy #[naked] #[no_mangle] diff --git a/tests/codegen/naked-fn/aligned.rs b/tests/codegen/naked-fn/aligned.rs index 3bbd67981e5bf..d9dcd7f6c3ef7 100644 --- a/tests/codegen/naked-fn/aligned.rs +++ b/tests/codegen/naked-fn/aligned.rs @@ -6,15 +6,12 @@ #![feature(naked_functions, fn_align)] use std::arch::naked_asm; -// CHECK: Function Attrs: naked -// CHECK-NEXT: define{{.*}}void @naked_empty() -// CHECK: align 16 +// CHECK: .balign 16 +// CHECK-LABEL: naked_empty: #[repr(align(16))] #[no_mangle] #[naked] pub unsafe extern "C" fn naked_empty() { - // CHECK-NEXT: start: - // CHECK-NEXT: call void asm - // CHECK-NEXT: unreachable + // CHECK: ret naked_asm!("ret"); } diff --git a/tests/codegen/naked-fn/generics.rs b/tests/codegen/naked-fn/generics.rs index 5bb40cddea56c..23c7766203b9f 100644 --- a/tests/codegen/naked-fn/generics.rs +++ b/tests/codegen/naked-fn/generics.rs @@ -4,7 +4,7 @@ #![crate_type = "lib"] #![feature(naked_functions, asm_const)] -use std::arch::asm; +use std::arch::naked_asm; #[no_mangle] fn test(x: u64) { @@ -33,7 +33,7 @@ pub extern "C" fn using_const_generics(x: u64) -> u64 { const M: u64 = 42; unsafe { - asm!( + naked_asm!( "xor rax, rax", "add rax, rdi", "add rax, {}", @@ -41,7 +41,6 @@ pub extern "C" fn using_const_generics(x: u64) -> u64 { "ret", const N, const M, - options(noreturn), ) } } @@ -64,11 +63,10 @@ impl Invert for i64 { #[naked] pub extern "C" fn generic_function(x: i64) -> i64 { unsafe { - asm!( + naked_asm!( "call {}", "ret", sym ::invert, - options(noreturn), ) } } @@ -85,7 +83,7 @@ impl Foo { #[naked] #[no_mangle] extern "C" fn method(self) -> u64 { - unsafe { asm!("mov rax, rdi", "ret", options(noreturn)) } + unsafe { naked_asm!("mov rax, rdi", "ret") } } } @@ -101,7 +99,7 @@ impl Bar for Foo { #[naked] #[no_mangle] extern "C" fn trait_method(self) -> u64 { - unsafe { asm!("mov rax, rdi", "ret", options(noreturn)) } + unsafe { naked_asm!("mov rax, rdi", "ret") } } } @@ -113,5 +111,5 @@ impl Bar for Foo { #[naked] #[no_mangle] pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize { - asm!("lea rax, [rdi + rsi]", "ret", options(noreturn)); + naked_asm!("lea rax, [rdi + rsi]", "ret"); } diff --git a/tests/codegen/naked-fn/instruction-set.rs b/tests/codegen/naked-fn/instruction-set.rs index 509c24f9457cf..5b790b2034c09 100644 --- a/tests/codegen/naked-fn/instruction-set.rs +++ b/tests/codegen/naked-fn/instruction-set.rs @@ -1,12 +1,15 @@ -//@ compile-flags: --target armv5te-none-eabi -//@ needs-llvm-components: arm +//@ revisions: arm-mode thumb-mode +//@ [arm-mode] compile-flags: --target armv5te-none-eabi +//@ [thumb-mode] compile-flags: --target thumbv5te-none-eabi +//@ [arm-mode] needs-llvm-components: arm +//@ [thumb-mode] needs-llvm-components: arm #![crate_type = "lib"] #![feature(no_core, lang_items, rustc_attrs, naked_functions)] #![no_core] #[rustc_builtin_macro] -macro_rules! asm { +macro_rules! naked_asm { () => {}; } @@ -15,29 +18,42 @@ trait Sized {} #[lang = "copy"] trait Copy {} +// arm-mode: .arm +// thumb-mode: .thumb // CHECK-LABEL: test_unspecified: -// CHECK: .arm +// CHECK: bx lr +// CHECK: .popsection +// arm-mode: .arm +// thumb-mode: .thumb #[no_mangle] #[naked] unsafe extern "C" fn test_unspecified() { - asm!("bx lr", options(noreturn)); + naked_asm!("bx lr"); } -// CHECK-LABEL: test_thumb: // CHECK: .thumb // CHECK: .thumb_func +// CHECK-LABEL: test_thumb: +// CHECK: bx lr +// CHECK: .popsection +// arm-mode: .arm +// thumb-mode: .thumb #[no_mangle] #[naked] #[instruction_set(arm::t32)] unsafe extern "C" fn test_thumb() { - asm!("bx lr", options(noreturn)); + naked_asm!("bx lr"); } -// CHECK-LABEL: test_arm: // CHECK: .arm +// CHECK-LABEL: test_arm: +// CHECK: bx lr +// CHECK: .popsection +// arm-mode: .arm +// thumb-mode: .thumb #[no_mangle] #[naked] -#[instruction_set(arm::t32)] +#[instruction_set(arm::a32)] unsafe extern "C" fn test_arm() { - asm!("bx lr", options(noreturn)); + naked_asm!("bx lr"); } diff --git a/tests/codegen/naked-fn/naked-functions.rs b/tests/codegen/naked-fn/naked-functions.rs index 8b12693da8f66..f505d27d48c62 100644 --- a/tests/codegen/naked-fn/naked-functions.rs +++ b/tests/codegen/naked-fn/naked-functions.rs @@ -1,9 +1,9 @@ -//@ revisions: linux windows macos thumb +//@ revisions: linux win macos thumb // //@[linux] compile-flags: --target x86_64-unknown-linux-gnu //@[linux] needs-llvm-components: x86 -//@[windows] compile-flags: --target x86_64-pc-windows-gnu -//@[windows] needs-llvm-components: x86 +//@[win] compile-flags: --target x86_64-pc-windows-gnu +//@[win] needs-llvm-components: x86 //@[macos] compile-flags: --target aarch64-apple-darwin //@[macos] needs-llvm-components: arm //@[thumb] compile-flags: --target thumbv7em-none-eabi @@ -23,16 +23,16 @@ trait Sized {} #[lang = "copy"] trait Copy {} -// linux,windows: .intel_syntax +// linux,win: .intel_syntax // // linux: .pushsection .text.naked_empty,\22ax\22, @progbits // macos: .pushsection __TEXT,__text,regular,pure_instructions -// windows: .pushsection .text.naked_empty,\22xr\22 +// win: .pushsection .text.naked_empty,\22xr\22 // thumb: .pushsection .text.naked_empty,\22ax\22, %progbits // // CHECK: .balign 4 // -// linux,windows,thumb: .globl naked_empty +// linux,win,thumb: .globl naked_empty // macos: .globl _naked_empty // // CHECK-NOT: .private_extern @@ -40,10 +40,10 @@ trait Copy {} // // linux: .type naked_empty, @function // -// windows: .def naked_empty -// windows: .scl 2 -// windows: .type 32 -// windows: .endef naked_empty +// win: .def naked_empty +// win: .scl 2 +// win: .type 32 +// win: .endef naked_empty // // thumb: .type naked_empty, %function // thumb: .thumb @@ -51,14 +51,14 @@ trait Copy {} // // CHECK-LABEL: naked_empty: // -// linux,macos,windows: ret +// linux,macos,win: ret // thumb: bx lr // // CHECK: .popsection // // thumb: .thumb // -// linux,windows: .att_syntax +// linux,win: .att_syntax #[no_mangle] #[naked] @@ -70,16 +70,16 @@ pub unsafe extern "C" fn naked_empty() { naked_asm!("bx lr"); } -// linux,windows: .intel_syntax +// linux,win: .intel_syntax // // linux: .pushsection .text.naked_with_args_and_return,\22ax\22, @progbits // macos: .pushsection __TEXT,__text,regular,pure_instructions -// windows: .pushsection .text.naked_with_args_and_return,\22xr\22 +// win: .pushsection .text.naked_with_args_and_return,\22xr\22 // thumb: .pushsection .text.naked_with_args_and_return,\22ax\22, %progbits // // CHECK: .balign 4 // -// linux,windows,thumb: .globl naked_with_args_and_return +// linux,win,thumb: .globl naked_with_args_and_return // macos: .globl _naked_with_args_and_return // // CHECK-NOT: .private_extern @@ -87,10 +87,10 @@ pub unsafe extern "C" fn naked_empty() { // // linux: .type naked_with_args_and_return, @function // -// windows: .def naked_with_args_and_return -// windows: .scl 2 -// windows: .type 32 -// windows: .endef naked_with_args_and_return +// win: .def naked_with_args_and_return +// win: .scl 2 +// win: .type 32 +// win: .endef naked_with_args_and_return // // thumb: .type naked_with_args_and_return, %function // thumb: .thumb @@ -98,18 +98,18 @@ pub unsafe extern "C" fn naked_empty() { // // CHECK-LABEL: naked_with_args_and_return: // -// linux, windows: lea rax, [rdi + rsi] +// linux, win: lea rax, [rdi + rsi] // macos: add x0, x0, x1 // thumb: adds r0, r0, r1 // -// linux,macos,windows: ret +// linux,macos,win: ret // thumb: bx lr // // CHECK: .popsection // // thumb: .thumb // -// linux,windows: .att_syntax +// linux,win: .att_syntax #[no_mangle] #[naked] @@ -132,7 +132,7 @@ pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize // linux: .pushsection .text.some_different_name,\22ax\22, @progbits // macos: .pushsection .text.some_different_name,regular,pure_instructions -// windows: .pushsection .text.some_different_name,\22xr\22 +// win: .pushsection .text.some_different_name,\22xr\22 // thumb: .pushsection .text.some_different_name,\22ax\22, %progbits // CHECK-LABEL: test_link_section: #[no_mangle] @@ -140,8 +140,8 @@ pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize #[link_section = ".text.some_different_name"] pub unsafe extern "C" fn test_link_section() { #[cfg(not(all(target_arch = "arm", target_feature = "thumb-mode")))] - naked_asm!("ret", options(noreturn)); + naked_asm!("ret"); #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] - naked_asm!("bx lr", options(noreturn)); + naked_asm!("bx lr"); } From b118d05f5797e255febfc11822506bf898224973 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 30 Nov 2024 20:22:56 +0100 Subject: [PATCH 16/24] Extend documentation for `missing_doc_code_examples` rustdoc lint in the rustdoc book --- src/doc/rustdoc/src/unstable-features.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index e9524c0b78d53..db8426492eec0 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -47,6 +47,10 @@ It can be enabled using: #![deny(rustdoc::missing_doc_code_examples)] ``` +It is not emitted for items that cannot be instantiated/called such as fields, variants, modules, +associated trait/impl items, impl blocks, statics and constants. +It is also not emitted for foreign items, aliases, extern crates and imports. + ## Extensions to the `#[doc]` attribute These features operate by extending the `#[doc]` attribute, and thus can be caught by the compiler From 64420b6ea81d9f7065421add9843ba22e876cd6c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 30 Nov 2024 15:36:03 +0100 Subject: [PATCH 17/24] skip `predefine_fn` for naked functions --- compiler/rustc_codegen_llvm/src/mono_item.rs | 2 +- .../rustc_codegen_ssa/src/mir/naked_asm.rs | 40 +++++++++++++++++-- compiler/rustc_codegen_ssa/src/mono_item.rs | 9 ++--- compiler/rustc_middle/src/mir/mono.rs | 22 ---------- 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index b91941396ed81..33789c6261f10 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -58,8 +58,8 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty()); let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance)); - let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); llvm::set_linkage(lldecl, base::linkage_to_llvm(linkage)); + let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); base::set_link_section(lldecl, attrs); if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR) && self.tcx.sess.target.supports_comdat() diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 65470dd6d5371..359cda19b8449 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,5 +1,5 @@ use rustc_attr::InstructionSetAttr; -use rustc_middle::mir::mono::{MonoItem, MonoItemData, Visibility}; +use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility}; use rustc_middle::mir::{Body, InlineAsmOperand}; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf}; use rustc_middle::ty::{Instance, TyCtxt}; @@ -153,6 +153,30 @@ fn prefix_and_suffix<'tcx>( ("", "") }; + let emit_fatal = |msg| tcx.dcx().span_fatal(tcx.def_span(instance.def_id()), msg); + + // see https://godbolt.org/z/cPK4sxKor. + // None means the default, which corresponds to internal linkage + let linkage = match item_data.linkage { + Linkage::External => Some(".globl"), + Linkage::LinkOnceAny => Some(".weak"), + Linkage::LinkOnceODR => Some(".weak"), + Linkage::WeakAny => Some(".weak"), + Linkage::WeakODR => Some(".weak"), + Linkage::Internal => None, + Linkage::Private => None, + Linkage::Appending => emit_fatal("Only global variables can have appending linkage!"), + Linkage::Common => emit_fatal("Functions may not have common linkage"), + Linkage::AvailableExternally => { + // this would make the function equal an extern definition + emit_fatal("Functions may not have available_externally linkage") + } + Linkage::ExternalWeak => { + // FIXME: actually this causes a SIGILL in LLVM + emit_fatal("Functions may not have external weak linkage") + } + }; + let mut begin = String::new(); let mut end = String::new(); match AsmBinaryFormat::from_target(&tcx.sess.target) { @@ -171,7 +195,9 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, ".pushsection {section},\"ax\", {progbits}").unwrap(); writeln!(begin, ".balign {align}").unwrap(); - writeln!(begin, ".globl {asm_name}").unwrap(); + if let Some(linkage) = linkage { + writeln!(begin, "{linkage} {asm_name}").unwrap(); + } if let Visibility::Hidden = item_data.visibility { writeln!(begin, ".hidden {asm_name}").unwrap(); } @@ -181,6 +207,8 @@ fn prefix_and_suffix<'tcx>( } writeln!(begin, "{asm_name}:").unwrap(); + eprintln!("{}", &begin); + writeln!(end).unwrap(); writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap(); writeln!(end, ".popsection").unwrap(); @@ -192,7 +220,9 @@ fn prefix_and_suffix<'tcx>( let section = link_section.unwrap_or("__TEXT,__text".to_string()); writeln!(begin, ".pushsection {},regular,pure_instructions", section).unwrap(); writeln!(begin, ".balign {align}").unwrap(); - writeln!(begin, ".globl {asm_name}").unwrap(); + if let Some(linkage) = linkage { + writeln!(begin, "{linkage} {asm_name}").unwrap(); + } if let Visibility::Hidden = item_data.visibility { writeln!(begin, ".private_extern {asm_name}").unwrap(); } @@ -208,7 +238,9 @@ fn prefix_and_suffix<'tcx>( let section = link_section.unwrap_or(format!(".text.{asm_name}")); writeln!(begin, ".pushsection {},\"xr\"", section).unwrap(); writeln!(begin, ".balign {align}").unwrap(); - writeln!(begin, ".globl {asm_name}").unwrap(); + if let Some(linkage) = linkage { + writeln!(begin, "{linkage} {asm_name}").unwrap(); + } writeln!(begin, ".def {asm_name}").unwrap(); writeln!(begin, ".scl 2").unwrap(); writeln!(begin, ".type 32").unwrap(); diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 085ef1cd8ea36..70ad5bee3e09c 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -137,13 +137,12 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { } MonoItem::Fn(instance) => { let attrs = cx.tcx().codegen_fn_attrs(instance.def_id()); - let linkage = if attrs.flags.contains(CodegenFnAttrFlags::NAKED) { - linkage_info.into_naked_linkage() + + if attrs.flags.contains(CodegenFnAttrFlags::NAKED) { + // do not define this function; it will become a global assembly block } else { - linkage_info.into_linkage() + cx.predefine_fn(instance, linkage_info.into_linkage(), visibility, symbol_name); }; - - cx.predefine_fn(instance, linkage, visibility, symbol_name); } MonoItem::GlobalAsm(..) => {} } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 4287b2c574ffe..01c66ecb570cb 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -299,28 +299,6 @@ impl LinkageInfo { Self::ImplicitInternal => Linkage::Internal, } } - - /// Linkage when the MonoItem is a naked function - /// - /// Naked functions are generated as a separate declaration (effectively an extern fn) and - /// definition (using global assembly). To link them together, some flavor of external linkage - /// must be used. - /// - /// This should be just an implementation detail of the backend, which is why this translation - /// is made at the final moment. - pub const fn into_naked_linkage(self) -> Linkage { - match self { - // promote Weak linkage to ExternalWeak - Self::Explicit(Linkage::WeakAny | Linkage::WeakODR) => Linkage::ExternalWeak, - Self::Explicit(linkage) => linkage, - - // the "implicit" means that linkage is picked by the partitioning algorithm. - // picking external should always be valid (given that we are in fact linking - // to the global assembly in the same CGU) - Self::ImplicitExternal => Linkage::External, - Self::ImplicitInternal => Linkage::External, - } - } } /// Specifies the linkage type for a `MonoItem`. From cf738f6bfba45aa65e96eb2c9cce3d0ecd25f66c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 30 Nov 2024 15:42:04 +0100 Subject: [PATCH 18/24] Revert "add `LinkageInfo` to keep track of how we figured out the linkage" This reverts commit 9142cae7d5b86ac70f815c32c0f3b1223b62a947. --- .../rustc_codegen_cranelift/src/driver/mod.rs | 2 +- compiler/rustc_codegen_gcc/src/base.rs | 2 +- compiler/rustc_codegen_llvm/src/base.rs | 2 +- .../src/back/symbol_export.rs | 4 +- .../rustc_codegen_ssa/src/mir/naked_asm.rs | 2 - compiler/rustc_codegen_ssa/src/mono_item.rs | 10 ++-- compiler/rustc_middle/src/mir/mono.rs | 27 +--------- .../rustc_monomorphize/src/partitioning.rs | 49 +++++++++---------- 8 files changed, 34 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/driver/mod.rs b/compiler/rustc_codegen_cranelift/src/driver/mod.rs index dbc913528f44a..fb0eed07c1971 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/mod.rs @@ -30,7 +30,7 @@ fn predefine_mono_items<'tcx>( get_function_sig(tcx, module.target_config().default_call_conv, instance); let linkage = crate::linkage::get_clif_linkage( mono_item, - data.linkage_info.into_linkage(), + data.linkage, data.visibility, is_compiler_builtins, ); diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 844871a714671..18aa32754e1d9 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -213,7 +213,7 @@ pub fn compile_codegen_unit( let mono_items = cgu.items_in_deterministic_order(tcx); for &(mono_item, data) in &mono_items { - mono_item.predefine::>(&cx, data.linkage_info, data.visibility); + mono_item.predefine::>(&cx, data.linkage, data.visibility); } // ... and now that we have everything pre-defined, fill out those definitions. diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 82c3cfe9aa04a..f62310bd94808 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -86,7 +86,7 @@ pub(crate) fn compile_codegen_unit( let cx = CodegenCx::new(tcx, cgu, &llvm_module); let mono_items = cx.codegen_unit.items_in_deterministic_order(cx.tcx); for &(mono_item, data) in &mono_items { - mono_item.predefine::>(&cx, data.linkage_info, data.visibility); + mono_item.predefine::>(&cx, data.linkage, data.visibility); } // ... and now that we have everything pre-defined, fill out those definitions. diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 0321a244f8683..788a8a13b3ee4 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -283,7 +283,7 @@ fn exported_symbols_provider_local( } if tcx.local_crate_exports_generics() { - use rustc_middle::mir::mono::{MonoItem, Visibility}; + use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility}; use rustc_middle::ty::InstanceKind; // Normally, we require that shared monomorphizations are not hidden, @@ -298,7 +298,7 @@ fn exported_symbols_provider_local( // The symbols created in this loop are sorted below it #[allow(rustc::potential_query_instability)] for (mono_item, data) in cgus.iter().flat_map(|cgu| cgu.items().iter()) { - if !data.linkage_info.is_external() { + if data.linkage != Linkage::External { // We can only re-use things with external linkage, otherwise // we'll get a linker error continue; diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 359cda19b8449..0767c002c5e06 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -207,8 +207,6 @@ fn prefix_and_suffix<'tcx>( } writeln!(begin, "{asm_name}:").unwrap(); - eprintln!("{}", &begin); - writeln!(end).unwrap(); writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap(); writeln!(end, ".popsection").unwrap(); diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 70ad5bee3e09c..6749bc63327e4 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -1,7 +1,7 @@ use rustc_hir as hir; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::mir::mono::{LinkageInfo, MonoItem, Visibility}; +use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility}; use rustc_middle::ty::Instance; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::{span_bug, ty}; @@ -15,7 +15,7 @@ pub trait MonoItemExt<'a, 'tcx> { fn predefine>( &self, cx: &'a Bx::CodegenCx, - linkage_info: LinkageInfo, + linkage: Linkage, visibility: Visibility, ); fn to_raw_string(&self) -> String; @@ -117,7 +117,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { fn predefine>( &self, cx: &'a Bx::CodegenCx, - linkage_info: LinkageInfo, + linkage: Linkage, visibility: Visibility, ) { debug!( @@ -133,7 +133,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { match *self { MonoItem::Static(def_id) => { - cx.predefine_static(def_id, linkage_info.into_linkage(), visibility, symbol_name); + cx.predefine_static(def_id, linkage, visibility, symbol_name); } MonoItem::Fn(instance) => { let attrs = cx.tcx().codegen_fn_attrs(instance.def_id()); @@ -141,7 +141,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { if attrs.flags.contains(CodegenFnAttrFlags::NAKED) { // do not define this function; it will become a global assembly block } else { - cx.predefine_fn(instance, linkage_info.into_linkage(), visibility, symbol_name); + cx.predefine_fn(instance, linkage, visibility, symbol_name); }; } MonoItem::GlobalAsm(..) => {} diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 01c66ecb570cb..1f50b67cb50d5 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -269,38 +269,13 @@ pub struct MonoItemData { /// `GloballyShared` maps to `false` and `LocalCopy` maps to `true`. pub inlined: bool, - pub linkage_info: LinkageInfo, + pub linkage: Linkage, pub visibility: Visibility, /// A cached copy of the result of `MonoItem::size_estimate`. pub size_estimate: usize, } -/// Stores how we know what linkage to use -#[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] -pub enum LinkageInfo { - /// The linkage was specified explicitly (e.g. using #[linkage = "..."]) - Explicit(Linkage), - /// Assume the symbol may be used from other CGUs, a safe default - ImplicitExternal, - /// We did not find any uses from other CGUs, so it's fine to make this internal - ImplicitInternal, -} - -impl LinkageInfo { - pub const fn is_external(self) -> bool { - matches!(self.into_linkage(), Linkage::External) - } - - pub const fn into_linkage(self) -> Linkage { - match self { - Self::Explicit(linkage) => linkage, - Self::ImplicitExternal => Linkage::External, - Self::ImplicitInternal => Linkage::Internal, - } - } -} - /// Specifies the linkage type for a `MonoItem`. /// /// See for more details about these variants. diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 2fd89e389e547..7ea4ded2b052c 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -109,8 +109,8 @@ use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; use rustc_middle::mir::mono::{ - CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, LinkageInfo, MonoItem, - MonoItemData, Visibility, + CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, MonoItem, MonoItemData, + Visibility, }; use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths}; use rustc_middle::ty::visit::TypeVisitableExt; @@ -245,7 +245,7 @@ where let cgu = codegen_units.entry(cgu_name).or_insert_with(|| CodegenUnit::new(cgu_name)); let mut can_be_internalized = true; - let (linkage_info, visibility) = mono_item_linkage_info_and_visibility( + let (linkage, visibility) = mono_item_linkage_and_visibility( cx.tcx, &mono_item, &mut can_be_internalized, @@ -259,7 +259,7 @@ where cgu.items_mut().insert(mono_item, MonoItemData { inlined: false, - linkage_info, + linkage, visibility, size_estimate, }); @@ -278,7 +278,7 @@ where // This is a CGU-private copy. cgu.items_mut().entry(inlined_item).or_insert_with(|| MonoItemData { inlined: true, - linkage_info: LinkageInfo::ImplicitInternal, + linkage: Linkage::Internal, visibility: Visibility::Default, size_estimate: inlined_item.size_estimate(cx.tcx), }); @@ -589,8 +589,7 @@ fn internalize_symbols<'tcx>( // If we got here, we did not find any uses from other CGUs, so // it's fine to make this monomorphization internal. - debug_assert_eq!(data.linkage_info, LinkageInfo::ImplicitExternal); - data.linkage_info = LinkageInfo::ImplicitInternal; + data.linkage = Linkage::Internal; data.visibility = Visibility::Default; } } @@ -608,7 +607,7 @@ fn mark_code_coverage_dead_code_cgu<'tcx>(codegen_units: &mut [CodegenUnit<'tcx> // function symbols to be included via `-u` or `/include` linker args. let dead_code_cgu = codegen_units .iter_mut() - .filter(|cgu| cgu.items().iter().any(|(_, data)| data.linkage_info.is_external())) + .filter(|cgu| cgu.items().iter().any(|(_, data)| data.linkage == Linkage::External)) .min_by_key(|cgu| cgu.size_estimate()); // If there are no CGUs that have externally linked items, then we just @@ -737,26 +736,24 @@ fn fallback_cgu_name(name_builder: &mut CodegenUnitNameBuilder<'_>) -> Symbol { name_builder.build_cgu_name(LOCAL_CRATE, &["fallback"], Some("cgu")) } -fn mono_item_linkage_info_and_visibility<'tcx>( +fn mono_item_linkage_and_visibility<'tcx>( tcx: TyCtxt<'tcx>, mono_item: &MonoItem<'tcx>, can_be_internalized: &mut bool, can_export_generics: bool, always_export_generics: bool, -) -> (LinkageInfo, Visibility) { +) -> (Linkage, Visibility) { if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) { - (LinkageInfo::Explicit(explicit_linkage), Visibility::Default) - } else { - let vis = mono_item_visibility( - tcx, - mono_item, - can_be_internalized, - can_export_generics, - always_export_generics, - ); - - (LinkageInfo::ImplicitExternal, vis) + return (explicit_linkage, Visibility::Default); } + let vis = mono_item_visibility( + tcx, + mono_item, + can_be_internalized, + can_export_generics, + always_export_generics, + ); + (Linkage::External, vis) } type CguNameCache = UnordMap<(DefId, bool), Symbol>; @@ -1036,7 +1033,7 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit< writeln!(s, " - items: {num_items}, mean size: {mean_size:.1}, sizes: {sizes}",); for (item, data) in cgu.items_in_deterministic_order(tcx) { - let linkage_info = data.linkage_info; + let linkage = data.linkage; let symbol_name = item.symbol_name(tcx).name; let symbol_hash_start = symbol_name.rfind('h'); let symbol_hash = symbol_hash_start.map_or("", |i| &symbol_name[i..]); @@ -1044,7 +1041,7 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit< let size = data.size_estimate; let _ = with_no_trimmed_paths!(writeln!( s, - " - {item} [{linkage_info:?}] [{symbol_hash}] ({kind}, size: {size})" + " - {item} [{linkage:?}] [{symbol_hash}] ({kind}, size: {size})" )); } @@ -1197,7 +1194,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co for cgu in codegen_units { for (&mono_item, &data) in cgu.items() { - item_to_cgus.entry(mono_item).or_default().push((cgu.name(), data.linkage_info)); + item_to_cgus.entry(mono_item).or_default().push((cgu.name(), data.linkage)); } } @@ -1210,11 +1207,11 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty); cgus.sort_by_key(|(name, _)| *name); cgus.dedup(); - for &(ref cgu_name, linkage_info) in cgus.iter() { + for &(ref cgu_name, linkage) in cgus.iter() { output.push(' '); output.push_str(cgu_name.as_str()); - let linkage_abbrev = match linkage_info.into_linkage() { + let linkage_abbrev = match linkage { Linkage::External => "External", Linkage::AvailableExternally => "Available", Linkage::LinkOnceAny => "OnceAny", From 805649b6482f65dfcbdc7dfc96d4b14f5bf1ad99 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 1 Dec 2024 03:01:00 +0000 Subject: [PATCH 19/24] Check let source before suggesting annotation --- compiler/rustc_hir_typeck/src/fallback.rs | 3 ++- ...lint-breaking-2024-assign-underscore.fixed | 17 ++++++++++++ .../lint-breaking-2024-assign-underscore.rs | 17 ++++++++++++ ...int-breaking-2024-assign-underscore.stderr | 26 +++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 tests/ui/never_type/lint-breaking-2024-assign-underscore.fixed create mode 100644 tests/ui/never_type/lint-breaking-2024-assign-underscore.rs create mode 100644 tests/ui/never_type/lint-breaking-2024-assign-underscore.stderr diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 7719facccc7ba..ddd146fe785e3 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -705,7 +705,8 @@ impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> { fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) -> Self::Result { // For a local, try suggest annotating the type if it's missing. - if let None = local.ty + if let hir::LocalSource::Normal = local.source + && let None = local.ty && let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(local.hir_id) && let Some(vid) = self.fcx.root_vid(ty) && self.reachable_vids.contains(&vid) diff --git a/tests/ui/never_type/lint-breaking-2024-assign-underscore.fixed b/tests/ui/never_type/lint-breaking-2024-assign-underscore.fixed new file mode 100644 index 0000000000000..f9f2b59a8c285 --- /dev/null +++ b/tests/ui/never_type/lint-breaking-2024-assign-underscore.fixed @@ -0,0 +1,17 @@ +//@ run-rustfix + +#![allow(unused)] +#![deny(dependency_on_unit_never_type_fallback)] + +fn foo() -> Result { + Err(()) +} + +fn test() -> Result<(), ()> { + //~^ ERROR this function depends on never type fallback being `()` + //~| WARN this was previously accepted by the compiler but is being phased out + _ = foo::<()>()?; + Ok(()) +} + +fn main() {} diff --git a/tests/ui/never_type/lint-breaking-2024-assign-underscore.rs b/tests/ui/never_type/lint-breaking-2024-assign-underscore.rs new file mode 100644 index 0000000000000..8a2f3d311ab90 --- /dev/null +++ b/tests/ui/never_type/lint-breaking-2024-assign-underscore.rs @@ -0,0 +1,17 @@ +//@ run-rustfix + +#![allow(unused)] +#![deny(dependency_on_unit_never_type_fallback)] + +fn foo() -> Result { + Err(()) +} + +fn test() -> Result<(), ()> { + //~^ ERROR this function depends on never type fallback being `()` + //~| WARN this was previously accepted by the compiler but is being phased out + _ = foo()?; + Ok(()) +} + +fn main() {} diff --git a/tests/ui/never_type/lint-breaking-2024-assign-underscore.stderr b/tests/ui/never_type/lint-breaking-2024-assign-underscore.stderr new file mode 100644 index 0000000000000..dc4ffa0d6f4ec --- /dev/null +++ b/tests/ui/never_type/lint-breaking-2024-assign-underscore.stderr @@ -0,0 +1,26 @@ +error: this function depends on never type fallback being `()` + --> $DIR/lint-breaking-2024-assign-underscore.rs:10:1 + | +LL | fn test() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see issue #123748 + = help: specify the types explicitly +note: in edition 2024, the requirement `!: Default` will fail + --> $DIR/lint-breaking-2024-assign-underscore.rs:13:9 + | +LL | _ = foo()?; + | ^^^^^ +note: the lint level is defined here + --> $DIR/lint-breaking-2024-assign-underscore.rs:4:9 + | +LL | #![deny(dependency_on_unit_never_type_fallback)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: use `()` annotations to avoid fallback changes + | +LL | _ = foo::<()>()?; + | ++++++ + +error: aborting due to 1 previous error + From 17c6efa9788cf9396750c4edacd4e724e8a7bb2b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 24 Nov 2024 01:44:16 +0000 Subject: [PATCH 20/24] Disentangle hir node match logic in adjust_fulfillment_errors --- .../src/fn_ctxt/adjust_fulfillment_errors.rs | 111 ++++++++++-------- 1 file changed, 65 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index b09d78614c321..eb564f96bc029 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -94,71 +94,82 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.find_ambiguous_parameter_in(def_id, error.root_obligation.predicate); } - let (expr, qpath) = match self.tcx.hir_node(hir_id) { - hir::Node::Expr(expr) => { - if self.closure_span_overlaps_error(error, expr.span) { + match self.tcx.hir_node(hir_id) { + hir::Node::Expr(&hir::Expr { kind: hir::ExprKind::Path(qpath), span, .. }) => { + if self.closure_span_overlaps_error(error, span) { return false; } - let qpath = - if let hir::ExprKind::Path(qpath) = expr.kind { Some(qpath) } else { None }; - (Some(&expr.kind), qpath) - } - hir::Node::Ty(hir::Ty { kind: hir::TyKind::Path(qpath), .. }) => (None, Some(*qpath)), - _ => return false, - }; + if let Some(param) = predicate_self_type_to_point_at + && self.point_at_path_if_possible(error, def_id, param, &qpath) + { + return true; + } - if let Some(qpath) = qpath { - // Prefer pointing at the turbofished arg that corresponds to the - // self type of the failing predicate over anything else. - if let Some(param) = predicate_self_type_to_point_at - && self.point_at_path_if_possible(error, def_id, param, &qpath) - { - return true; - } + if let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Call(callee, args), + hir_id: call_hir_id, + span: call_span, + .. + }) = self.tcx.parent_hir_node(hir_id) + && callee.hir_id == hir_id + { + if self.closure_span_overlaps_error(error, *call_span) { + return false; + } - if let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Call(callee, args), - hir_id: call_hir_id, - span: call_span, - .. - }) = self.tcx.parent_hir_node(hir_id) - && callee.hir_id == hir_id - { - if self.closure_span_overlaps_error(error, *call_span) { - return false; + for param in + [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + .into_iter() + .flatten() + { + if self.blame_specific_arg_if_possible( + error, + def_id, + param, + *call_hir_id, + callee.span, + None, + args, + ) { + return true; + } + } } for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] .into_iter() .flatten() { - if self.blame_specific_arg_if_possible( - error, - def_id, - param, - *call_hir_id, - callee.span, - None, - args, - ) { + if self.point_at_path_if_possible(error, def_id, param, &qpath) { return true; } } } - - for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + hir::Node::Ty(hir::Ty { kind: hir::TyKind::Path(qpath), .. }) => { + for param in [ + predicate_self_type_to_point_at, + param_to_point_at, + fallback_param_to_point_at, + self_param_to_point_at, + ] .into_iter() .flatten() - { - if self.point_at_path_if_possible(error, def_id, param, &qpath) { - return true; + { + if self.point_at_path_if_possible(error, def_id, param, &qpath) { + return true; + } } } - } + hir::Node::Expr(&hir::Expr { + kind: hir::ExprKind::MethodCall(segment, receiver, args, ..), + span, + .. + }) => { + if self.closure_span_overlaps_error(error, span) { + return false; + } - match expr { - Some(hir::ExprKind::MethodCall(segment, receiver, args, ..)) => { if let Some(param) = predicate_self_type_to_point_at && self.point_at_generic_if_possible(error, def_id, param, segment) { @@ -208,7 +219,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return true; } } - Some(hir::ExprKind::Struct(qpath, fields, ..)) => { + hir::Node::Expr(&hir::Expr { + kind: hir::ExprKind::Struct(qpath, fields, ..), + span, + .. + }) => { + if self.closure_span_overlaps_error(error, span) { + return false; + } + if let Res::Def(DefKind::Struct | DefKind::Variant, variant_def_id) = self.typeck_results.borrow().qpath_res(qpath, hir_id) { From 30afeb0357006bf87c8f41f067896e3c0abe11dd Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 24 Nov 2024 01:15:04 +0000 Subject: [PATCH 21/24] Adjust HostEffect error spans correctly to point at args --- compiler/rustc_hir_typeck/src/callee.rs | 19 +++- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 3 +- .../src/fn_ctxt/adjust_fulfillment_errors.rs | 107 +++++++++++++++--- compiler/rustc_hir_typeck/src/place_op.rs | 2 +- compiler/rustc_middle/src/traits/mod.rs | 4 + .../src/error_reporting/traits/suggestions.rs | 5 +- .../ui/consts/const-block-const-bound.stderr | 12 +- ...constifconst-call-in-const-position.stderr | 4 +- tests/ui/consts/fn_trait_refs.stderr | 75 ++++++++++-- .../impl-trait/normalize-tait-in-const.stderr | 12 +- ...assoc-type-const-bound-usage-fail-2.stderr | 4 +- .../assoc-type-const-bound-usage-fail.stderr | 4 +- .../const-traits/call-const-closure.stderr | 4 +- .../call-const-in-tilde-const.stderr | 2 +- .../call-const-trait-method-fail.stderr | 2 +- .../call-generic-method-nonconst.stderr | 12 +- .../const-default-method-bodies.stderr | 4 +- .../const-traits/const-drop-bound.stderr | 12 +- .../const-drop-fail-2.precise.stderr | 16 +-- .../const-drop-fail-2.stock.stderr | 16 +-- .../const-drop-fail.precise.stderr | 39 +++---- .../ui/traits/const-traits/const-drop-fail.rs | 4 +- .../const-traits/const-drop-fail.stock.stderr | 39 +++---- .../const-traits/const-opaque.no.stderr | 16 ++- .../const-traits/cross-crate.gatednc.stderr | 4 +- ...-method-body-is-const-body-checking.stderr | 10 +- ...-method-body-is-const-same-trait-ck.stderr | 4 +- .../effects/minicore-fn-fail.stderr | 12 +- .../effects/no-explicit-const-params.stderr | 4 +- .../specializing-constness-2.stderr | 4 +- .../super-traits-fail-2.yn.stderr | 4 +- .../super-traits-fail-2.yy.stderr | 4 +- .../super-traits-fail-3.yn.stderr | 4 +- .../tilde-const-and-const-params.stderr | 4 +- .../trait-where-clause-const.stderr | 18 ++- .../unsatisfied-const-trait-bound.stderr | 8 +- 36 files changed, 361 insertions(+), 136 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index a92482e6a0e39..b0afbab8e01b8 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -4,7 +4,7 @@ use rustc_ast::util::parser::PREC_UNAMBIGUOUS; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey}; use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem}; +use rustc_hir::{self as hir, HirId, LangItem}; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer; use rustc_infer::traits::{self, Obligation, ObligationCause, ObligationCauseCode}; @@ -428,7 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let (fn_sig, def_id) = match *callee_ty.kind() { ty::FnDef(def_id, args) => { - self.enforce_context_effects(call_expr.span, def_id, args); + self.enforce_context_effects(Some(call_expr.hir_id), call_expr.span, def_id, args); let fn_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, args); // Unit testing: function items annotated with @@ -837,6 +837,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[tracing::instrument(level = "debug", skip(self, span))] pub(super) fn enforce_context_effects( &self, + call_hir_id: Option, span: Span, callee_did: DefId, callee_args: GenericArgsRef<'tcx>, @@ -867,10 +868,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.tcx.is_conditionally_const(callee_did) { let q = self.tcx.const_conditions(callee_did); // FIXME(const_trait_impl): Use this span with a better cause code. - for (cond, _) in q.instantiate(self.tcx, callee_args) { + for (idx, (cond, pred_span)) in + q.instantiate(self.tcx, callee_args).into_iter().enumerate() + { + let cause = self.cause( + span, + if let Some(hir_id) = call_hir_id { + ObligationCauseCode::HostEffectInExpr(callee_did, pred_span, hir_id, idx) + } else { + ObligationCauseCode::WhereClause(callee_did, pred_span) + }, + ); self.register_predicate(Obligation::new( self.tcx, - self.misc(span), + cause, self.param_env, cond.to_host_effect_clause(self.tcx, host), )); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 47af8c681da00..76bbcfd1312db 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -185,7 +185,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, method: MethodCallee<'tcx>, ) { - self.enforce_context_effects(span, method.def_id, method.args); + self.enforce_context_effects(Some(hir_id), span, method.def_id, method.args); self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id))); self.write_args(hir_id, method.args); } @@ -263,6 +263,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Adjust::Deref(Some(overloaded_deref)) => { self.enforce_context_effects( + None, expr.span, overloaded_deref.method_call(self.tcx), self.tcx.mk_args(&[a.target.into()]), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index eb564f96bc029..3dd82249d3308 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -11,26 +11,56 @@ use rustc_trait_selection::traits; use crate::FnCtxt; +enum ClauseFlavor { + /// Predicate comes from `predicates_of`. + Where, + /// Predicate comes from `const_conditions`. + Const, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn adjust_fulfillment_error_for_expr_obligation( &self, error: &mut traits::FulfillmentError<'tcx>, ) -> bool { - let ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) = - *error.obligation.cause.code().peel_derives() - else { - return false; + let (def_id, hir_id, idx, flavor) = match *error.obligation.cause.code().peel_derives() { + ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) => { + (def_id, hir_id, idx, ClauseFlavor::Where) + } + ObligationCauseCode::HostEffectInExpr(def_id, _, hir_id, idx) => { + (def_id, hir_id, idx, ClauseFlavor::Const) + } + _ => return false, }; - let Some(uninstantiated_pred) = self - .tcx - .predicates_of(def_id) - .instantiate_identity(self.tcx) - .predicates - .into_iter() - .nth(idx) - else { - return false; + let uninstantiated_pred = match flavor { + ClauseFlavor::Where => { + if let Some(pred) = self + .tcx + .predicates_of(def_id) + .instantiate_identity(self.tcx) + .predicates + .into_iter() + .nth(idx) + { + pred + } else { + return false; + } + } + ClauseFlavor::Const => { + if let Some((pred, _)) = self + .tcx + .const_conditions(def_id) + .instantiate_identity(self.tcx) + .into_iter() + .nth(idx) + { + pred.to_host_effect_clause(self.tcx, ty::BoundConstness::Maybe) + } else { + return false; + } + } }; let generics = self.tcx.generics_of(def_id); @@ -39,6 +69,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::ClauseKind::Trait(pred) => { (pred.trait_ref.args.to_vec(), Some(pred.self_ty().into())) } + ty::ClauseKind::HostEffect(pred) => { + (pred.trait_ref.args.to_vec(), Some(pred.self_ty().into())) + } ty::ClauseKind::Projection(pred) => (pred.projection_term.args.to_vec(), None), ty::ClauseKind::ConstArgHasType(arg, ty) => (vec![ty.into(), arg.into()], None), ty::ClauseKind::ConstEvaluatable(e) => (vec![e.into()], None), @@ -95,6 +128,51 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } match self.tcx.hir_node(hir_id) { + hir::Node::Expr(&hir::Expr { + kind: + hir::ExprKind::Call( + hir::Expr { kind: hir::ExprKind::Path(qpath), span: callee_span, .. }, + args, + ), + span, + .. + }) => { + if self.closure_span_overlaps_error(error, span) { + return false; + } + + if let Some(param) = predicate_self_type_to_point_at + && self.point_at_path_if_possible(error, def_id, param, &qpath) + { + return true; + } + + for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + .into_iter() + .flatten() + { + if self.blame_specific_arg_if_possible( + error, + def_id, + param, + hir_id, + *callee_span, + None, + args, + ) { + return true; + } + } + + for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] + .into_iter() + .flatten() + { + if self.point_at_path_if_possible(error, def_id, param, &qpath) { + return true; + } + } + } hir::Node::Expr(&hir::Expr { kind: hir::ExprKind::Path(qpath), span, .. }) => { if self.closure_span_overlaps_error(error, span) { return false; @@ -544,7 +622,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, ) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> { match obligation_cause_code { - traits::ObligationCauseCode::WhereClauseInExpr(_, _, _, _) => { + traits::ObligationCauseCode::WhereClauseInExpr(_, _, _, _) + | ObligationCauseCode::HostEffectInExpr(..) => { // This is the "root"; we assume that the `expr` is already pointing here. // Therefore, we return `Ok` so that this `expr` can be refined further. Ok(expr) diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 3d401cef76f7d..7c667511aa997 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -296,7 +296,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); }; *deref = OverloadedDeref { mutbl, span: deref.span }; - self.enforce_context_effects(expr.span, method.def_id, method.args); + self.enforce_context_effects(None, expr.span, method.def_id, method.args); // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514). // This helps avoid accidental drops. if inside_union diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index d61ef7641ee9f..0a77c3bc42fb9 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -204,6 +204,10 @@ pub enum ObligationCauseCode<'tcx> { /// list of the item. WhereClauseInExpr(DefId, Span, HirId, usize), + /// Like `WhereClauseinExpr`, but indexes into the `const_conditions` + /// rather than the `predicates_of`. + HostEffectInExpr(DefId, Span, HirId, usize), + /// A type like `&'a T` is WF only if `T: 'a`. ReferenceOutlivesReferent(Ty<'tcx>), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 27b45f7094645..2bb503f17b4ed 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2803,6 +2803,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ObligationCauseCode::WhereClause(item_def_id, span) | ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..) + | ObligationCauseCode::HostEffectInExpr(item_def_id, span, ..) if !span.is_dummy() => { if let ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, pos) = &cause_code { @@ -2966,7 +2967,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.help(help); } } - ObligationCauseCode::WhereClause(..) | ObligationCauseCode::WhereClauseInExpr(..) => { + ObligationCauseCode::WhereClause(..) + | ObligationCauseCode::WhereClauseInExpr(..) + | ObligationCauseCode::HostEffectInExpr(..) => { // We hold the `DefId` of the item introducing the obligation, but displaying it // doesn't add user usable information. It always point at an associated item. } diff --git a/tests/ui/consts/const-block-const-bound.stderr b/tests/ui/consts/const-block-const-bound.stderr index b2b2f62c58f27..0931eff2175bc 100644 --- a/tests/ui/consts/const-block-const-bound.stderr +++ b/tests/ui/consts/const-block-const-bound.stderr @@ -1,8 +1,16 @@ error[E0277]: the trait bound `UnconstDrop: const Destruct` is not satisfied - --> $DIR/const-block-const-bound.rs:18:9 + --> $DIR/const-block-const-bound.rs:18:11 | LL | f(UnconstDrop); - | ^^^^^^^^^^^^^^ + | - ^^^^^^^^^^^ + | | + | required by a bound introduced by this call + | +note: required by a bound in `f` + --> $DIR/const-block-const-bound.rs:8:15 + | +LL | const fn f(x: T) {} + | ^^^^^^ required by this bound in `f` error: aborting due to 1 previous error diff --git a/tests/ui/consts/constifconst-call-in-const-position.stderr b/tests/ui/consts/constifconst-call-in-const-position.stderr index 6add83dc52c57..c778299560fab 100644 --- a/tests/ui/consts/constifconst-call-in-const-position.stderr +++ b/tests/ui/consts/constifconst-call-in-const-position.stderr @@ -2,13 +2,13 @@ error[E0277]: the trait bound `T: const Tr` is not satisfied --> $DIR/constifconst-call-in-const-position.rs:17:38 | LL | const fn foo() -> [u8; T::a()] { - | ^^^^^^ + | ^ error[E0277]: the trait bound `T: const Tr` is not satisfied --> $DIR/constifconst-call-in-const-position.rs:18:9 | LL | [0; T::a()] - | ^^^^^^ + | ^ error: aborting due to 2 previous errors diff --git a/tests/ui/consts/fn_trait_refs.stderr b/tests/ui/consts/fn_trait_refs.stderr index 108500217139e..11e13c3efdd19 100644 --- a/tests/ui/consts/fn_trait_refs.stderr +++ b/tests/ui/consts/fn_trait_refs.stderr @@ -121,34 +121,89 @@ LL | T: ~const FnMut<()> + ~const Destruct, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `fn() -> i32 {one}: const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:70:24 + --> $DIR/fn_trait_refs.rs:70:32 | LL | let test_one = test_fn(one); - | ^^^^^^^^^^^^ + | ------- ^^^ + | | + | required by a bound introduced by this call + | +note: required by a bound in `test_fn` + --> $DIR/fn_trait_refs.rs:35:24 + | +LL | const fn test_fn(mut f: T) -> (T::Output, T::Output, T::Output) + | ------- required by a bound in this function +LL | where +LL | T: ~const Fn<()> + ~const Destruct, + | ^^^^^^ required by this bound in `test_fn` error[E0277]: the trait bound `fn() -> i32 {two}: const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:73:24 + --> $DIR/fn_trait_refs.rs:73:36 | LL | let test_two = test_fn_mut(two); - | ^^^^^^^^^^^^^^^^ + | ----------- ^^^ + | | + | required by a bound introduced by this call + | +note: required by a bound in `test_fn_mut` + --> $DIR/fn_trait_refs.rs:49:27 + | +LL | const fn test_fn_mut(mut f: T) -> (T::Output, T::Output) + | ----------- required by a bound in this function +LL | where +LL | T: ~const FnMut<()> + ~const Destruct, + | ^^^^^^ required by this bound in `test_fn_mut` error[E0277]: the trait bound `&T: ~const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:39:9 + --> $DIR/fn_trait_refs.rs:39:19 | LL | tester_fn(&f), - | ^^^^^^^^^^^^^ + | --------- ^^ + | | + | required by a bound introduced by this call + | +note: required by a bound in `tester_fn` + --> $DIR/fn_trait_refs.rs:14:24 + | +LL | const fn tester_fn(f: T) -> T::Output + | --------- required by a bound in this function +LL | where +LL | T: ~const Fn<()> + ~const Destruct, + | ^^^^^^ required by this bound in `tester_fn` error[E0277]: the trait bound `&T: ~const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:41:9 + --> $DIR/fn_trait_refs.rs:41:23 | LL | tester_fn_mut(&f), - | ^^^^^^^^^^^^^^^^^ + | ------------- ^^ + | | + | required by a bound introduced by this call + | +note: required by a bound in `tester_fn_mut` + --> $DIR/fn_trait_refs.rs:21:27 + | +LL | const fn tester_fn_mut(mut f: T) -> T::Output + | ------------- required by a bound in this function +LL | where +LL | T: ~const FnMut<()> + ~const Destruct, + | ^^^^^^ required by this bound in `tester_fn_mut` error[E0277]: the trait bound `&mut T: ~const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:53:9 + --> $DIR/fn_trait_refs.rs:53:23 | LL | tester_fn_mut(&mut f), - | ^^^^^^^^^^^^^^^^^^^^^ + | ------------- ^^^^^^ + | | + | required by a bound introduced by this call + | +note: required by a bound in `tester_fn_mut` + --> $DIR/fn_trait_refs.rs:21:27 + | +LL | const fn tester_fn_mut(mut f: T) -> T::Output + | ------------- required by a bound in this function +LL | where +LL | T: ~const FnMut<()> + ~const Destruct, + | ^^^^^^ required by this bound in `tester_fn_mut` error[E0015]: cannot call non-const closure in constant functions --> $DIR/fn_trait_refs.rs:16:5 diff --git a/tests/ui/impl-trait/normalize-tait-in-const.stderr b/tests/ui/impl-trait/normalize-tait-in-const.stderr index bb874cbe41b33..203fbfc1d2c55 100644 --- a/tests/ui/impl-trait/normalize-tait-in-const.stderr +++ b/tests/ui/impl-trait/normalize-tait-in-const.stderr @@ -13,10 +13,18 @@ LL | const fn with_positive ~const Fn(&'a Alias<'a>) + ~const Destruc = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `for<'a, 'b> fn(&'a foo::Alias<'b>) {foo}: const Destruct` is not satisfied - --> $DIR/normalize-tait-in-const.rs:33:5 + --> $DIR/normalize-tait-in-const.rs:33:19 | LL | with_positive(foo); - | ^^^^^^^^^^^^^^^^^^ + | ------------- ^^^ + | | + | required by a bound introduced by this call + | +note: required by a bound in `with_positive` + --> $DIR/normalize-tait-in-const.rs:26:62 + | +LL | const fn with_positive ~const Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { + | ^^^^^^ required by this bound in `with_positive` error[E0015]: cannot call non-const closure in constant functions --> $DIR/normalize-tait-in-const.rs:27:5 diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.stderr index 86bd07a5f593d..c7af0a220ca31 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.stderr +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.stderr @@ -2,13 +2,13 @@ error[E0277]: the trait bound `::Assoc: ~const Trait` is not sati --> $DIR/assoc-type-const-bound-usage-fail-2.rs:23:5 | LL | T::Assoc::::func(); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ error[E0277]: the trait bound `::Assoc: ~const Trait` is not satisfied --> $DIR/assoc-type-const-bound-usage-fail-2.rs:25:5 | LL | ::Assoc::::func(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.stderr index 145fe2c41dd40..99fc924ad06b9 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.stderr +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.stderr @@ -2,13 +2,13 @@ error[E0277]: the trait bound `T: ~const Trait` is not satisfied --> $DIR/assoc-type-const-bound-usage-fail.rs:16:5 | LL | T::Assoc::func(); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^ error[E0277]: the trait bound `T: ~const Trait` is not satisfied --> $DIR/assoc-type-const-bound-usage-fail.rs:18:5 | LL | ::Assoc::func(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/call-const-closure.stderr b/tests/ui/traits/const-traits/call-const-closure.stderr index 3fed67f5d0884..fe7c115aaab44 100644 --- a/tests/ui/traits/const-traits/call-const-closure.stderr +++ b/tests/ui/traits/const-traits/call-const-closure.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `(): ~const Bar` is not satisfied - --> $DIR/call-const-closure.rs:17:15 + --> $DIR/call-const-closure.rs:17:18 | LL | (const || ().foo())(); - | ^^^^^^^^ + | ^^^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/call-const-in-tilde-const.stderr b/tests/ui/traits/const-traits/call-const-in-tilde-const.stderr index e56968b90972d..b9dabceb5de47 100644 --- a/tests/ui/traits/const-traits/call-const-in-tilde-const.stderr +++ b/tests/ui/traits/const-traits/call-const-in-tilde-const.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `T: const Foo` is not satisfied --> $DIR/call-const-in-tilde-const.rs:9:13 | LL | const { T::foo() } - | ^^^^^^^^ + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/call-const-trait-method-fail.stderr b/tests/ui/traits/const-traits/call-const-trait-method-fail.stderr index b461fd9e39e3c..64850335c2ab6 100644 --- a/tests/ui/traits/const-traits/call-const-trait-method-fail.stderr +++ b/tests/ui/traits/const-traits/call-const-trait-method-fail.stderr @@ -2,7 +2,7 @@ error[E0277]: the trait bound `u32: ~const Plus` is not satisfied --> $DIR/call-const-trait-method-fail.rs:26:5 | LL | a.plus(b) - | ^^^^^^^^^ + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/call-generic-method-nonconst.stderr b/tests/ui/traits/const-traits/call-generic-method-nonconst.stderr index d881bd5f4de61..74a22186a1637 100644 --- a/tests/ui/traits/const-traits/call-generic-method-nonconst.stderr +++ b/tests/ui/traits/const-traits/call-generic-method-nonconst.stderr @@ -1,8 +1,16 @@ error[E0277]: the trait bound `S: const Foo` is not satisfied - --> $DIR/call-generic-method-nonconst.rs:24:22 + --> $DIR/call-generic-method-nonconst.rs:24:34 | LL | pub const EQ: bool = equals_self(&S); - | ^^^^^^^^^^^^^^^ + | ----------- ^^ + | | + | required by a bound introduced by this call + | +note: required by a bound in `equals_self` + --> $DIR/call-generic-method-nonconst.rs:17:25 + | +LL | const fn equals_self(t: &T) -> bool { + | ^^^^^^ required by this bound in `equals_self` error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/const-default-method-bodies.stderr b/tests/ui/traits/const-traits/const-default-method-bodies.stderr index 5879330f15823..903f7d37f9d8e 100644 --- a/tests/ui/traits/const-traits/const-default-method-bodies.stderr +++ b/tests/ui/traits/const-traits/const-default-method-bodies.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `NonConstImpl: ~const ConstDefaultFn` is not satisfied - --> $DIR/const-default-method-bodies.rs:25:5 + --> $DIR/const-default-method-bodies.rs:25:18 | LL | NonConstImpl.a(); - | ^^^^^^^^^^^^^^^^ + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/const-drop-bound.stderr b/tests/ui/traits/const-traits/const-drop-bound.stderr index 60718cc84c101..78ba0279566de 100644 --- a/tests/ui/traits/const-traits/const-drop-bound.stderr +++ b/tests/ui/traits/const-traits/const-drop-bound.stderr @@ -1,8 +1,16 @@ error[E0277]: the trait bound `Foo: ~const Destruct` is not satisfied - --> $DIR/const-drop-bound.rs:23:5 + --> $DIR/const-drop-bound.rs:23:9 | LL | foo(res) - | ^^^^^^^^ + | --- ^^^ + | | + | required by a bound introduced by this call + | +note: required by a bound in `foo` + --> $DIR/const-drop-bound.rs:9:61 + | +LL | const fn foo(res: Result) -> Option where E: ~const Destruct { + | ^^^^^^ required by this bound in `foo` error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr b/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr index bb9966c7ec393..7b2cafb612441 100644 --- a/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr @@ -1,12 +1,14 @@ error[E0277]: the trait bound `ConstDropImplWithBounds: const Destruct` is not satisfied - --> $DIR/const-drop-fail-2.rs:31:15 + --> $DIR/const-drop-fail-2.rs:31:23 | -LL | const _: () = check::>( - | _______________^ -LL | | -LL | | ConstDropImplWithBounds(PhantomData) -LL | | ); - | |_^ +LL | const _: () = check::>( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail-2.rs:21:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr b/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr index bb9966c7ec393..7b2cafb612441 100644 --- a/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr @@ -1,12 +1,14 @@ error[E0277]: the trait bound `ConstDropImplWithBounds: const Destruct` is not satisfied - --> $DIR/const-drop-fail-2.rs:31:15 + --> $DIR/const-drop-fail-2.rs:31:23 | -LL | const _: () = check::>( - | _______________^ -LL | | -LL | | ConstDropImplWithBounds(PhantomData) -LL | | ); - | |_^ +LL | const _: () = check::>( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail-2.rs:21:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/const-drop-fail.precise.stderr b/tests/ui/traits/const-traits/const-drop-fail.precise.stderr index 67e774fbd0597..8b3e777a0b098 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.precise.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail.precise.stderr @@ -1,31 +1,32 @@ error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:27:23 + --> $DIR/const-drop-fail.rs:32:5 | -LL | const _: () = check($exp); - | ^^^^^^^^^^^ +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call ... -LL | / check_all! { -LL | | NonTrivialDrop, -LL | | ConstImplWithDropGlue(NonTrivialDrop), -LL | | } - | |_- in this macro invocation +LL | NonTrivialDrop, + | ^^^^^^^^^^^^^^ | - = note: this error originates in the macro `check_all` (in Nightly builds, run with -Z macro-backtrace for more info) +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:23:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:27:23 + --> $DIR/const-drop-fail.rs:34:5 | -LL | const _: () = check($exp); - | ^^^^^^^^^^^ +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call ... -LL | / check_all! { -LL | | NonTrivialDrop, -LL | | ConstImplWithDropGlue(NonTrivialDrop), -LL | | } - | |_- in this macro invocation +LL | ConstImplWithDropGlue(NonTrivialDrop), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:23:19 | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the macro `check_all` (in Nightly builds, run with -Z macro-backtrace for more info) +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/const-drop-fail.rs b/tests/ui/traits/const-traits/const-drop-fail.rs index 08435266e1f80..5e05b9db474a6 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.rs +++ b/tests/ui/traits/const-traits/const-drop-fail.rs @@ -25,14 +25,14 @@ const fn check(_: T) {} macro_rules! check_all { ($($exp:expr),*$(,)?) => {$( const _: () = check($exp); - //~^ ERROR the trait bound `NonTrivialDrop: const Destruct` is not satisfied - //~| ERROR the trait bound `NonTrivialDrop: const Destruct` is not satisfied )*}; } check_all! { NonTrivialDrop, + //~^ ERROR the trait bound `NonTrivialDrop: const Destruct` is not satisfied ConstImplWithDropGlue(NonTrivialDrop), + //~^ ERROR the trait bound `NonTrivialDrop: const Destruct` is not satisfied } fn main() {} diff --git a/tests/ui/traits/const-traits/const-drop-fail.stock.stderr b/tests/ui/traits/const-traits/const-drop-fail.stock.stderr index 67e774fbd0597..8b3e777a0b098 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.stock.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail.stock.stderr @@ -1,31 +1,32 @@ error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:27:23 + --> $DIR/const-drop-fail.rs:32:5 | -LL | const _: () = check($exp); - | ^^^^^^^^^^^ +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call ... -LL | / check_all! { -LL | | NonTrivialDrop, -LL | | ConstImplWithDropGlue(NonTrivialDrop), -LL | | } - | |_- in this macro invocation +LL | NonTrivialDrop, + | ^^^^^^^^^^^^^^ | - = note: this error originates in the macro `check_all` (in Nightly builds, run with -Z macro-backtrace for more info) +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:23:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:27:23 + --> $DIR/const-drop-fail.rs:34:5 | -LL | const _: () = check($exp); - | ^^^^^^^^^^^ +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call ... -LL | / check_all! { -LL | | NonTrivialDrop, -LL | | ConstImplWithDropGlue(NonTrivialDrop), -LL | | } - | |_- in this macro invocation +LL | ConstImplWithDropGlue(NonTrivialDrop), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:23:19 | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the macro `check_all` (in Nightly builds, run with -Z macro-backtrace for more info) +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/const-opaque.no.stderr b/tests/ui/traits/const-traits/const-opaque.no.stderr index e43a6b603fda1..1278e1257467b 100644 --- a/tests/ui/traits/const-traits/const-opaque.no.stderr +++ b/tests/ui/traits/const-traits/const-opaque.no.stderr @@ -1,14 +1,22 @@ error[E0277]: the trait bound `(): const Foo` is not satisfied - --> $DIR/const-opaque.rs:31:18 + --> $DIR/const-opaque.rs:31:22 | LL | let opaque = bar(()); - | ^^^^^^^ + | --- ^^ + | | + | required by a bound introduced by this call + | +note: required by a bound in `bar` + --> $DIR/const-opaque.rs:26:17 + | +LL | const fn bar(t: T) -> impl ~const Foo { + | ^^^^^^ required by this bound in `bar` error[E0277]: the trait bound `(): const Foo` is not satisfied - --> $DIR/const-opaque.rs:33:5 + --> $DIR/const-opaque.rs:33:12 | LL | opaque.method(); - | ^^^^^^^^^^^^^^^ + | ^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/cross-crate.gatednc.stderr b/tests/ui/traits/const-traits/cross-crate.gatednc.stderr index b6f2434140d6a..4d5abf643a8c2 100644 --- a/tests/ui/traits/const-traits/cross-crate.gatednc.stderr +++ b/tests/ui/traits/const-traits/cross-crate.gatednc.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `cross_crate::NonConst: ~const cross_crate::MyTrait` is not satisfied - --> $DIR/cross-crate.rs:19:5 + --> $DIR/cross-crate.rs:19:14 | LL | NonConst.func(); - | ^^^^^^^^^^^^^^^ + | ^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/default-method-body-is-const-body-checking.stderr b/tests/ui/traits/const-traits/default-method-body-is-const-body-checking.stderr index 0534f3eb8d292..8c284bde67e7f 100644 --- a/tests/ui/traits/const-traits/default-method-body-is-const-body-checking.stderr +++ b/tests/ui/traits/const-traits/default-method-body-is-const-body-checking.stderr @@ -1,8 +1,14 @@ error[E0277]: the trait bound `(): ~const Tr` is not satisfied - --> $DIR/default-method-body-is-const-body-checking.rs:12:9 + --> $DIR/default-method-body-is-const-body-checking.rs:12:15 | LL | foo::<()>(); - | ^^^^^^^^^^^ + | ^^ + | +note: required by a bound in `foo` + --> $DIR/default-method-body-is-const-body-checking.rs:7:28 + | +LL | const fn foo() where T: ~const Tr {} + | ^^^^^^ required by this bound in `foo` error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/default-method-body-is-const-same-trait-ck.stderr b/tests/ui/traits/const-traits/default-method-body-is-const-same-trait-ck.stderr index d987cad6f1451..2bd71c940e736 100644 --- a/tests/ui/traits/const-traits/default-method-body-is-const-same-trait-ck.stderr +++ b/tests/ui/traits/const-traits/default-method-body-is-const-same-trait-ck.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `(): ~const Tr` is not satisfied - --> $DIR/default-method-body-is-const-same-trait-ck.rs:9:9 + --> $DIR/default-method-body-is-const-same-trait-ck.rs:9:12 | LL | ().a() - | ^^^^^^ + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/effects/minicore-fn-fail.stderr b/tests/ui/traits/const-traits/effects/minicore-fn-fail.stderr index cf158643b3472..fa8be631a26bb 100644 --- a/tests/ui/traits/const-traits/effects/minicore-fn-fail.stderr +++ b/tests/ui/traits/const-traits/effects/minicore-fn-fail.stderr @@ -1,8 +1,16 @@ error[E0277]: the trait bound `(): ~const Foo` is not satisfied - --> $DIR/minicore-fn-fail.rs:19:5 + --> $DIR/minicore-fn-fail.rs:19:19 | LL | call_indirect(&foo::<()>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ------------- ^^^^^^^^^^ + | | + | required by a bound introduced by this call + | +note: required by a bound in `call_indirect` + --> $DIR/minicore-fn-fail.rs:11:27 + | +LL | const fn call_indirect(t: &T) { t() } + | ^^^^^^ required by this bound in `call_indirect` error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/effects/no-explicit-const-params.stderr b/tests/ui/traits/const-traits/effects/no-explicit-const-params.stderr index 0b8e4696c4613..9bd2c2cb8da2a 100644 --- a/tests/ui/traits/const-traits/effects/no-explicit-const-params.stderr +++ b/tests/ui/traits/const-traits/effects/no-explicit-const-params.stderr @@ -27,10 +27,10 @@ LL | trait Bar { | ^^^ error[E0277]: the trait bound `(): const Bar` is not satisfied - --> $DIR/no-explicit-const-params.rs:24:5 + --> $DIR/no-explicit-const-params.rs:24:6 | LL | <() as Bar>::bar(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^ error[E0107]: function takes 0 generic arguments but 1 generic argument was supplied --> $DIR/no-explicit-const-params.rs:15:5 diff --git a/tests/ui/traits/const-traits/specializing-constness-2.stderr b/tests/ui/traits/const-traits/specializing-constness-2.stderr index 4ad5e3157d4f8..edba836aac354 100644 --- a/tests/ui/traits/const-traits/specializing-constness-2.stderr +++ b/tests/ui/traits/const-traits/specializing-constness-2.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `T: ~const A` is not satisfied - --> $DIR/specializing-constness-2.rs:27:5 + --> $DIR/specializing-constness-2.rs:27:6 | LL | ::a(); - | ^^^^^^^^^^^^^ + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/super-traits-fail-2.yn.stderr b/tests/ui/traits/const-traits/super-traits-fail-2.yn.stderr index 01ae209016a12..ee49810bacec4 100644 --- a/tests/ui/traits/const-traits/super-traits-fail-2.yn.stderr +++ b/tests/ui/traits/const-traits/super-traits-fail-2.yn.stderr @@ -11,10 +11,10 @@ LL | trait Bar: ~const Foo {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `T: ~const Foo` is not satisfied - --> $DIR/super-traits-fail-2.rs:20:5 + --> $DIR/super-traits-fail-2.rs:20:7 | LL | x.a(); - | ^^^^^ + | ^ error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/super-traits-fail-2.yy.stderr b/tests/ui/traits/const-traits/super-traits-fail-2.yy.stderr index ae4c65e4aee1e..a213273c1c78d 100644 --- a/tests/ui/traits/const-traits/super-traits-fail-2.yy.stderr +++ b/tests/ui/traits/const-traits/super-traits-fail-2.yy.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `T: ~const Foo` is not satisfied - --> $DIR/super-traits-fail-2.rs:20:5 + --> $DIR/super-traits-fail-2.rs:20:7 | LL | x.a(); - | ^^^^^ + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/super-traits-fail-3.yn.stderr b/tests/ui/traits/const-traits/super-traits-fail-3.yn.stderr index 8fcada1bfd1d9..ecee348222d1f 100644 --- a/tests/ui/traits/const-traits/super-traits-fail-3.yn.stderr +++ b/tests/ui/traits/const-traits/super-traits-fail-3.yn.stderr @@ -25,10 +25,10 @@ LL | const fn foo(x: &T) { = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `T: ~const Foo` is not satisfied - --> $DIR/super-traits-fail-3.rs:24:5 + --> $DIR/super-traits-fail-3.rs:24:7 | LL | x.a(); - | ^^^^^ + | ^ error: aborting due to 4 previous errors diff --git a/tests/ui/traits/const-traits/tilde-const-and-const-params.stderr b/tests/ui/traits/const-traits/tilde-const-and-const-params.stderr index 78bf85e9c6dee..f77d63db054a0 100644 --- a/tests/ui/traits/const-traits/tilde-const-and-const-params.stderr +++ b/tests/ui/traits/const-traits/tilde-const-and-const-params.stderr @@ -26,13 +26,13 @@ error[E0277]: the trait bound `A: const Add42` is not satisfied --> $DIR/tilde-const-and-const-params.rs:27:61 | LL | fn bar(_: Foo) -> Foo<{ A::add(N) }> { - | ^^^^^^^^^ + | ^ error[E0277]: the trait bound `A: const Add42` is not satisfied --> $DIR/tilde-const-and-const-params.rs:9:44 | LL | fn add(self) -> Foo<{ A::add(N) }> { - | ^^^^^^^^^ + | ^ error: aborting due to 4 previous errors diff --git a/tests/ui/traits/const-traits/trait-where-clause-const.stderr b/tests/ui/traits/const-traits/trait-where-clause-const.stderr index d7735ef282f7f..4100ae1c6bfd7 100644 --- a/tests/ui/traits/const-traits/trait-where-clause-const.stderr +++ b/tests/ui/traits/const-traits/trait-where-clause-const.stderr @@ -2,13 +2,25 @@ error[E0277]: the trait bound `T: ~const Bar` is not satisfied --> $DIR/trait-where-clause-const.rs:21:5 | LL | T::b(); - | ^^^^^^ + | ^ + | +note: required by a bound in `Foo::b` + --> $DIR/trait-where-clause-const.rs:15:24 + | +LL | fn b() where Self: ~const Bar; + | ^^^^^^ required by this bound in `Foo::b` error[E0277]: the trait bound `T: ~const Bar` is not satisfied - --> $DIR/trait-where-clause-const.rs:23:5 + --> $DIR/trait-where-clause-const.rs:23:12 | LL | T::c::(); - | ^^^^^^^^^^^ + | ^ + | +note: required by a bound in `Foo::c` + --> $DIR/trait-where-clause-const.rs:16:13 + | +LL | fn c(); + | ^^^^^^ required by this bound in `Foo::c` error: aborting due to 2 previous errors diff --git a/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr b/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr index d04143fc4641d..bda6a029cc2a6 100644 --- a/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr +++ b/tests/ui/traits/const-traits/unsatisfied-const-trait-bound.stderr @@ -10,19 +10,19 @@ error[E0277]: the trait bound `T: const Trait` is not satisfied --> $DIR/unsatisfied-const-trait-bound.rs:29:37 | LL | fn accept0(_: Container<{ T::make() }>) {} - | ^^^^^^^^^ + | ^ error[E0277]: the trait bound `T: const Trait` is not satisfied --> $DIR/unsatisfied-const-trait-bound.rs:33:50 | LL | const fn accept1(_: Container<{ T::make() }>) {} - | ^^^^^^^^^ + | ^ error[E0277]: the trait bound `Ty: const Trait` is not satisfied - --> $DIR/unsatisfied-const-trait-bound.rs:22:5 + --> $DIR/unsatisfied-const-trait-bound.rs:22:15 | LL | require::(); - | ^^^^^^^^^^^^^^^ + | ^^ | note: required by a bound in `require` --> $DIR/unsatisfied-const-trait-bound.rs:8:15 From d5c5d58a37db02f533993187ba1f12f1bde32a54 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 1 Dec 2024 05:11:38 +0000 Subject: [PATCH 22/24] Pull out expr handling --- .../src/fn_ctxt/adjust_fulfillment_errors.rs | 190 +++++++++--------- 1 file changed, 97 insertions(+), 93 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index 3dd82249d3308..6eaba641888ca 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -128,21 +128,59 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } match self.tcx.hir_node(hir_id) { - hir::Node::Expr(&hir::Expr { - kind: - hir::ExprKind::Call( - hir::Expr { kind: hir::ExprKind::Path(qpath), span: callee_span, .. }, - args, - ), - span, - .. - }) => { - if self.closure_span_overlaps_error(error, span) { - return false; + hir::Node::Expr(expr) => self.point_at_expr_if_possible( + error, + def_id, + expr, + predicate_self_type_to_point_at, + param_to_point_at, + fallback_param_to_point_at, + self_param_to_point_at, + ), + + hir::Node::Ty(hir::Ty { kind: hir::TyKind::Path(qpath), .. }) => { + for param in [ + predicate_self_type_to_point_at, + param_to_point_at, + fallback_param_to_point_at, + self_param_to_point_at, + ] + .into_iter() + .flatten() + { + if self.point_at_path_if_possible(error, def_id, param, &qpath) { + return true; + } } + false + } + + _ => false, + } + } + + fn point_at_expr_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + callee_def_id: DefId, + expr: &'tcx hir::Expr<'tcx>, + predicate_self_type_to_point_at: Option>, + param_to_point_at: Option>, + fallback_param_to_point_at: Option>, + self_param_to_point_at: Option>, + ) -> bool { + if self.closure_span_overlaps_error(error, expr.span) { + return false; + } + + match expr.kind { + hir::ExprKind::Call( + hir::Expr { kind: hir::ExprKind::Path(qpath), span: callee_span, .. }, + args, + ) => { if let Some(param) = predicate_self_type_to_point_at - && self.point_at_path_if_possible(error, def_id, param, &qpath) + && self.point_at_path_if_possible(error, callee_def_id, param, &qpath) { return true; } @@ -153,9 +191,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { if self.blame_specific_arg_if_possible( error, - def_id, + callee_def_id, param, - hir_id, + expr.hir_id, *callee_span, None, args, @@ -168,88 +206,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .into_iter() .flatten() { - if self.point_at_path_if_possible(error, def_id, param, &qpath) { + if self.point_at_path_if_possible(error, callee_def_id, param, &qpath) { return true; } } } - hir::Node::Expr(&hir::Expr { kind: hir::ExprKind::Path(qpath), span, .. }) => { - if self.closure_span_overlaps_error(error, span) { - return false; + hir::ExprKind::Path(qpath) => { + // If the parent is an call, then process this as a call. + // + // This is because the `WhereClauseInExpr` obligations come from + // the well-formedness of the *path* expression, but we care to + // point at the call expression (namely, its args). + if let hir::Node::Expr( + call_expr @ hir::Expr { kind: hir::ExprKind::Call(callee, ..), .. }, + ) = self.tcx.parent_hir_node(expr.hir_id) + && callee.hir_id == expr.hir_id + { + return self.point_at_expr_if_possible( + error, + callee_def_id, + call_expr, + predicate_self_type_to_point_at, + param_to_point_at, + fallback_param_to_point_at, + self_param_to_point_at, + ); } + // Otherwise, just try to point at path components. + if let Some(param) = predicate_self_type_to_point_at - && self.point_at_path_if_possible(error, def_id, param, &qpath) + && self.point_at_path_if_possible(error, callee_def_id, param, &qpath) { return true; } - if let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Call(callee, args), - hir_id: call_hir_id, - span: call_span, - .. - }) = self.tcx.parent_hir_node(hir_id) - && callee.hir_id == hir_id - { - if self.closure_span_overlaps_error(error, *call_span) { - return false; - } - - for param in - [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] - .into_iter() - .flatten() - { - if self.blame_specific_arg_if_possible( - error, - def_id, - param, - *call_hir_id, - callee.span, - None, - args, - ) { - return true; - } - } - } - for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] .into_iter() .flatten() { - if self.point_at_path_if_possible(error, def_id, param, &qpath) { + if self.point_at_path_if_possible(error, callee_def_id, param, &qpath) { return true; } } } - hir::Node::Ty(hir::Ty { kind: hir::TyKind::Path(qpath), .. }) => { - for param in [ - predicate_self_type_to_point_at, - param_to_point_at, - fallback_param_to_point_at, - self_param_to_point_at, - ] - .into_iter() - .flatten() - { - if self.point_at_path_if_possible(error, def_id, param, &qpath) { - return true; - } - } - } - hir::Node::Expr(&hir::Expr { - kind: hir::ExprKind::MethodCall(segment, receiver, args, ..), - span, - .. - }) => { - if self.closure_span_overlaps_error(error, span) { - return false; - } - + hir::ExprKind::MethodCall(segment, receiver, args, ..) => { if let Some(param) = predicate_self_type_to_point_at - && self.point_at_generic_if_possible(error, def_id, param, segment) + && self.point_at_generic_if_possible(error, callee_def_id, param, segment) { // HACK: This is not correct, since `predicate_self_type_to_point_at` might // not actually correspond to the receiver of the method call. But we @@ -259,7 +262,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error.obligation.cause.map_code(|parent_code| { ObligationCauseCode::FunctionArg { arg_hir_id: receiver.hir_id, - call_hir_id: hir_id, + call_hir_id: expr.hir_id, parent_code, } }); @@ -272,9 +275,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { if self.blame_specific_arg_if_possible( error, - def_id, + callee_def_id, param, - hir_id, + expr.hir_id, segment.ident.span, Some(receiver), args, @@ -283,7 +286,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } if let Some(param_to_point_at) = param_to_point_at - && self.point_at_generic_if_possible(error, def_id, param_to_point_at, segment) + && self.point_at_generic_if_possible( + error, + callee_def_id, + param_to_point_at, + segment, + ) { return true; } @@ -297,25 +305,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return true; } } - hir::Node::Expr(&hir::Expr { - kind: hir::ExprKind::Struct(qpath, fields, ..), - span, - .. - }) => { - if self.closure_span_overlaps_error(error, span) { - return false; - } - + hir::ExprKind::Struct(qpath, fields, ..) => { if let Res::Def(DefKind::Struct | DefKind::Variant, variant_def_id) = - self.typeck_results.borrow().qpath_res(qpath, hir_id) + self.typeck_results.borrow().qpath_res(qpath, expr.hir_id) { for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] .into_iter() .flatten() { - let refined_expr = - self.point_at_field_if_possible(def_id, param, variant_def_id, fields); + let refined_expr = self.point_at_field_if_possible( + callee_def_id, + param, + variant_def_id, + fields, + ); match refined_expr { None => {} @@ -339,7 +343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .into_iter() .flatten() { - if self.point_at_path_if_possible(error, def_id, param, qpath) { + if self.point_at_path_if_possible(error, callee_def_id, param, qpath) { return true; } } From b87e935407c1d90845e21388ac569d7449e4293a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 26 Nov 2024 00:07:11 +0000 Subject: [PATCH 23/24] Revert "Reject raw lifetime followed by \' as well" This reverts commit 1990f1560801ca3f9e6a3286e58204aa329ee037. --- compiler/rustc_lexer/src/lib.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index b584e7afd98fa..6caeec1b5cc90 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -707,17 +707,7 @@ impl Cursor<'_> { self.bump(); self.bump(); self.eat_while(is_id_continue); - match self.first() { - '\'' => { - // Check if after skipping literal contents we've met a closing - // single quote (which means that user attempted to create a - // string with single quotes). - self.bump(); - let kind = Char { terminated: true }; - return Literal { kind, suffix_start: self.pos_within_token() }; - } - _ => return RawLifetime, - } + return RawLifetime; } // Either a lifetime or a character literal with From d878fd8877669005ee05de0c5c60021109580934 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 26 Nov 2024 00:28:55 +0000 Subject: [PATCH 24/24] Only error raw lifetime followed by \' in edition 2021+ --- compiler/rustc_parse/src/lexer/mod.rs | 23 +++++++++++++++++-- ...> immediately-followed-by-lt.e2021.stderr} | 2 +- .../raw/immediately-followed-by-lt.rs | 8 +++++-- 3 files changed, 28 insertions(+), 5 deletions(-) rename tests/ui/lifetimes/raw/{immediately-followed-by-lt.stderr => immediately-followed-by-lt.e2021.stderr} (84%) diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 8a42cf388f9b7..d97f05dc7eb74 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -300,6 +300,26 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let prefix_span = self.mk_sp(start, ident_start); if prefix_span.at_least_rust_2021() { + // If the raw lifetime is followed by \' then treat it a normal + // lifetime followed by a \', which is to interpret it as a character + // literal. In this case, it's always an invalid character literal + // since the literal must necessarily have >3 characters (r#...) inside + // of it, which is invalid. + if self.cursor.as_str().starts_with('\'') { + let lit_span = self.mk_sp(start, self.pos + BytePos(1)); + let contents = self.str_from_to(start + BytePos(1), self.pos); + emit_unescape_error( + self.dcx(), + contents, + lit_span, + lit_span, + Mode::Char, + 0..contents.len(), + EscapeError::MoreThanOneChar, + ) + .expect("expected error"); + } + let span = self.mk_sp(start, self.pos); let lifetime_name_without_tick = @@ -371,8 +391,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { rustc_lexer::TokenKind::Caret => token::BinOp(token::Caret), rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent), - rustc_lexer::TokenKind::Unknown - | rustc_lexer::TokenKind::InvalidIdent => { + rustc_lexer::TokenKind::Unknown | rustc_lexer::TokenKind::InvalidIdent => { // Don't emit diagnostics for sequences of the same invalid token if swallow_next_invalid > 0 { swallow_next_invalid -= 1; diff --git a/tests/ui/lifetimes/raw/immediately-followed-by-lt.stderr b/tests/ui/lifetimes/raw/immediately-followed-by-lt.e2021.stderr similarity index 84% rename from tests/ui/lifetimes/raw/immediately-followed-by-lt.stderr rename to tests/ui/lifetimes/raw/immediately-followed-by-lt.e2021.stderr index 1caeec84b22c9..e600cc37fc42f 100644 --- a/tests/ui/lifetimes/raw/immediately-followed-by-lt.stderr +++ b/tests/ui/lifetimes/raw/immediately-followed-by-lt.e2021.stderr @@ -1,5 +1,5 @@ error: character literal may only contain one codepoint - --> $DIR/immediately-followed-by-lt.rs:11:4 + --> $DIR/immediately-followed-by-lt.rs:15:4 | LL | w!('r#long'id); | ^^^^^^^^ diff --git a/tests/ui/lifetimes/raw/immediately-followed-by-lt.rs b/tests/ui/lifetimes/raw/immediately-followed-by-lt.rs index fe2b6de7bb3e2..eb161f9c85564 100644 --- a/tests/ui/lifetimes/raw/immediately-followed-by-lt.rs +++ b/tests/ui/lifetimes/raw/immediately-followed-by-lt.rs @@ -1,4 +1,8 @@ -//@ edition: 2021 +//@ revisions: e2015 e2021 + +//@[e2021] edition: 2021 +//@[e2015] edition: 2015 +//@[e2015] check-pass // Make sure we reject the case where a raw lifetime is immediately followed by another // lifetime. This reserves a modest amount of space for changing lexing to, for example, @@ -9,6 +13,6 @@ macro_rules! w { } w!('r#long'id); -//~^ ERROR character literal may only contain one codepoint +//[e2021]~^ ERROR character literal may only contain one codepoint fn main() {}