From 1990f1560801ca3f9e6a3286e58204aa329ee037 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 30 Oct 2024 01:10:33 +0000 Subject: [PATCH 01/11] Reject raw lifetime followed by \' as well --- compiler/rustc_lexer/src/lib.rs | 12 +++++++++++- .../ui/lifetimes/raw/immediately-followed-by-lt.rs | 14 ++++++++++++++ .../raw/immediately-followed-by-lt.stderr | 13 +++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/ui/lifetimes/raw/immediately-followed-by-lt.rs create mode 100644 tests/ui/lifetimes/raw/immediately-followed-by-lt.stderr diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index b0ab50dd77388..f9f2a14dbd275 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -715,7 +715,17 @@ impl Cursor<'_> { self.bump(); self.bump(); self.eat_while(is_id_continue); - return RawLifetime; + 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, + } } // Either a lifetime or a character literal with diff --git a/tests/ui/lifetimes/raw/immediately-followed-by-lt.rs b/tests/ui/lifetimes/raw/immediately-followed-by-lt.rs new file mode 100644 index 0000000000000..fe2b6de7bb3e2 --- /dev/null +++ b/tests/ui/lifetimes/raw/immediately-followed-by-lt.rs @@ -0,0 +1,14 @@ +//@ edition: 2021 + +// 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, +// delay rejection of overlong char literals like `'r#long'id`. + +macro_rules! w { + ($($tt:tt)*) => {} +} + +w!('r#long'id); +//~^ ERROR character literal may only contain one codepoint + +fn main() {} diff --git a/tests/ui/lifetimes/raw/immediately-followed-by-lt.stderr b/tests/ui/lifetimes/raw/immediately-followed-by-lt.stderr new file mode 100644 index 0000000000000..1caeec84b22c9 --- /dev/null +++ b/tests/ui/lifetimes/raw/immediately-followed-by-lt.stderr @@ -0,0 +1,13 @@ +error: character literal may only contain one codepoint + --> $DIR/immediately-followed-by-lt.rs:11:4 + | +LL | w!('r#long'id); + | ^^^^^^^^ + | +help: if you meant to write a string literal, use double quotes + | +LL | w!("r#long"id); + | ~ ~ + +error: aborting due to 1 previous error + From d7e6074083231aa8ccf7f299626fc83d59f78ee4 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 30 Oct 2024 13:16:43 -0700 Subject: [PATCH 02/11] style-guide: Only use the new binop heuristic for assignments This avoids pathological cases where chains of binops get progressively deeper. --- src/doc/style-guide/src/editions.md | 5 +++-- src/doc/style-guide/src/expressions.md | 12 +++--------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/doc/style-guide/src/editions.md b/src/doc/style-guide/src/editions.md index 74e873e35ff38..d9dba641495ab 100644 --- a/src/doc/style-guide/src/editions.md +++ b/src/doc/style-guide/src/editions.md @@ -40,8 +40,9 @@ include: of a delimited expression, delimited expressions are generally combinable, regardless of the number of members. Previously only applied with exactly one member (except for closures with explicit blocks). -- When line-breaking a binary operator, if the first operand spans multiple - lines, use the base indentation of the last line. +- When line-breaking an assignment operator, if the left-hand side spans + multiple lines, use the base indentation of the last line of the left-hand + side to indent the right-hand side. - Miscellaneous `rustfmt` bugfixes. - Use version-sort (sort `x8`, `x16`, `x32`, `x64`, `x128` in that order). - Change "ASCIIbetical" sort to Unicode-aware "non-lowercase before lowercase". diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index 3bb0ee6d5ff6c..4f63a632030b4 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -328,9 +328,9 @@ foo_bar Prefer line-breaking at an assignment operator (either `=` or `+=`, etc.) rather than at other binary operators. -If line-breaking at a binary operator (including assignment operators) where the -first operand spans multiple lines, use the base indentation of the *last* -line of the first operand, and indent relative to that: +If line-breaking an assignment operator where the left-hand side spans multiple +lines, use the base indentation of the *last* line of the left-hand side, and +indent the right-hand side relative to that: ```rust impl SomeType { @@ -341,12 +341,6 @@ impl SomeType { .extra_info = long_long_long_long_long_long_long_long_long_long_long_long_long_long_long; - self.array[array_index as usize] - .as_mut() - .expect("thing must exist") - .extra_info - + long_long_long_long_long_long_long_long_long_long_long_long_long_long_long; - self.array[array_index as usize] .as_mut() .expect("thing must exist") From ce3e14a448e090abf494a91b87f124258c542d4c Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 17 Oct 2024 20:11:20 +1100 Subject: [PATCH 03/11] Remove support for `-Zprofile` (gcov-style coverage instrumentation) --- compiler/rustc_codegen_llvm/src/attributes.rs | 5 ---- compiler/rustc_codegen_llvm/src/back/write.rs | 1 - .../src/debuginfo/metadata.rs | 26 ------------------ .../rustc_codegen_llvm/src/debuginfo/mod.rs | 3 --- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 - compiler/rustc_codegen_ssa/src/back/write.rs | 25 +++-------------- compiler/rustc_interface/src/tests.rs | 2 -- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 13 ++------- compiler/rustc_metadata/src/creader.rs | 4 +-- compiler/rustc_session/src/config.rs | 14 +--------- compiler/rustc_session/src/options.rs | 5 ---- src/doc/rustc/src/instrument-coverage.md | 8 ++---- .../src/compiler-flags/profile.md | 27 ------------------- tests/run-make/profile/rmake.rs | 21 --------------- tests/run-make/profile/test.rs | 1 - 15 files changed, 10 insertions(+), 146 deletions(-) delete mode 100644 src/doc/unstable-book/src/compiler-flags/profile.md delete mode 100644 tests/run-make/profile/rmake.rs delete mode 100644 tests/run-make/profile/test.rs diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 2c5ec9dad59f1..64bb22e8cb25e 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -232,11 +232,6 @@ fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { return None; } - // probestack doesn't play nice either with gcov profiling. - if cx.sess().opts.unstable_opts.profile { - return None; - } - let attr_value = match cx.sess().target.stack_probes { StackProbeType::None => return None, // Request LLVM to generate the probes inline. If the given LLVM version does not support diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index bfa9e8b82a033..cf7b16c9cc4f4 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -591,7 +591,6 @@ pub(crate) unsafe fn llvm_optimize( pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), config.instrument_coverage, instr_profile_output_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), - config.instrument_gcov, pgo_sample_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), config.debug_info_for_profiling, llvm_selfprofiler, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 9064cfaeb2987..0d1fd0163ebfd 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -7,7 +7,6 @@ use std::{iter, ptr}; use libc::{c_char, c_longlong, c_uint}; use rustc_codegen_ssa::debuginfo::type_names::{VTableNameKind, cpp_like_debuginfo}; use rustc_codegen_ssa::traits::*; -use rustc_fs_util::path_to_c_string; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::bug; @@ -979,33 +978,8 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( debug_name_table_kind, ); - if tcx.sess.opts.unstable_opts.profile { - let default_gcda_path = &output_filenames.with_extension("gcda"); - let gcda_path = - tcx.sess.opts.unstable_opts.profile_emit.as_ref().unwrap_or(default_gcda_path); - - let gcov_cu_info = [ - path_to_mdstring(debug_context.llcontext, &output_filenames.with_extension("gcno")), - path_to_mdstring(debug_context.llcontext, gcda_path), - unit_metadata, - ]; - let gcov_metadata = llvm::LLVMMDNodeInContext2( - debug_context.llcontext, - gcov_cu_info.as_ptr(), - gcov_cu_info.len(), - ); - let val = llvm::LLVMMetadataAsValue(debug_context.llcontext, gcov_metadata); - - llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, c"llvm.gcov".as_ptr(), val); - } - return unit_metadata; }; - - fn path_to_mdstring<'ll>(llcx: &'ll llvm::Context, path: &Path) -> &'ll llvm::Metadata { - let path_str = path_to_c_string(path); - unsafe { llvm::LLVMMDStringInContext2(llcx, path_str.as_ptr(), path_str.as_bytes().len()) } - } } /// Creates a `DW_TAG_member` entry inside the DIE represented by the given `type_di_node`. diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 72e723aa8491f..b6c20cdcf0cd4 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -55,7 +55,6 @@ const DW_TAG_arg_variable: c_uint = 0x101; /// A context object for maintaining all state needed by the debuginfo module. pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { - llcontext: &'ll llvm::Context, llmod: &'ll llvm::Module, builder: &'ll mut DIBuilder<'ll>, created_files: RefCell, &'ll DIFile>>, @@ -78,9 +77,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { debug!("CodegenUnitDebugContext::new"); let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) }; // DIBuilder inherits context from the module, so we'd better use the same one - let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; CodegenUnitDebugContext { - llcontext, llmod, builder, created_files: Default::default(), diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 8fc586d2c8ffc..5fad7583e1aee 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2269,7 +2269,6 @@ unsafe extern "C" { PGOUsePath: *const c_char, InstrumentCoverage: bool, InstrProfileOutput: *const c_char, - InstrumentGCOV: bool, PGOSampleUsePath: *const c_char, DebugInfoForProfiling: bool, llvm_selfprofiler: *mut c_void, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 8445d16befb3a..d977cca247eea 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -90,7 +90,6 @@ pub struct ModuleConfig { pub pgo_sample_use: Option, pub debug_info_for_profiling: bool, pub instrument_coverage: bool, - pub instrument_gcov: bool, pub sanitizer: SanitizerSet, pub sanitizer_recover: SanitizerSet, @@ -123,12 +122,7 @@ pub struct ModuleConfig { } impl ModuleConfig { - fn new( - kind: ModuleKind, - tcx: TyCtxt<'_>, - no_builtins: bool, - is_compiler_builtins: bool, - ) -> ModuleConfig { + fn new(kind: ModuleKind, tcx: TyCtxt<'_>, no_builtins: bool) -> ModuleConfig { // If it's a regular module, use `$regular`, otherwise use `$other`. // `$regular` and `$other` are evaluated lazily. macro_rules! if_regular { @@ -189,13 +183,6 @@ impl ModuleConfig { pgo_sample_use: if_regular!(sess.opts.unstable_opts.profile_sample_use.clone(), None), debug_info_for_profiling: sess.opts.unstable_opts.debug_info_for_profiling, instrument_coverage: if_regular!(sess.instrument_coverage(), false), - instrument_gcov: if_regular!( - // compiler_builtins overrides the codegen-units settings, - // which is incompatible with -Zprofile which requires that - // only a single codegen unit is used per crate. - sess.opts.unstable_opts.profile && !is_compiler_builtins, - false - ), sanitizer: if_regular!(sess.opts.unstable_opts.sanitizer, SanitizerSet::empty()), sanitizer_dataflow_abilist: if_regular!( @@ -473,16 +460,12 @@ pub(crate) fn start_async_codegen( let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins); - let is_compiler_builtins = attr::contains_name(crate_attrs, sym::compiler_builtins); let crate_info = CrateInfo::new(tcx, target_cpu); - let regular_config = - ModuleConfig::new(ModuleKind::Regular, tcx, no_builtins, is_compiler_builtins); - let metadata_config = - ModuleConfig::new(ModuleKind::Metadata, tcx, no_builtins, is_compiler_builtins); - let allocator_config = - ModuleConfig::new(ModuleKind::Allocator, tcx, no_builtins, is_compiler_builtins); + let regular_config = ModuleConfig::new(ModuleKind::Regular, tcx, no_builtins); + let metadata_config = ModuleConfig::new(ModuleKind::Metadata, tcx, no_builtins); + let allocator_config = ModuleConfig::new(ModuleKind::Allocator, tcx, no_builtins); let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); let (codegen_worker_send, codegen_worker_receive) = channel(); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index d3762e739db80..35bba149d0a6b 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -832,8 +832,6 @@ fn test_unstable_options_tracking_hash() { tracked!(polonius, Polonius::Legacy); tracked!(precise_enum_drop_elaboration, false); tracked!(print_fuel, Some("abc".to_string())); - tracked!(profile, true); - tracked!(profile_emit, Some(PathBuf::from("abc"))); tracked!(profile_sample_use, Some(PathBuf::from("abc"))); tracked!(profiler_runtime, "abc".to_string()); tracked!(regparm, Some(3)); diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 3e906f89c15c3..3b7dc6de82553 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -42,7 +42,6 @@ #if LLVM_VERSION_GE(19, 0) #include "llvm/Support/PGOOptions.h" #endif -#include "llvm/Transforms/Instrumentation/GCOVProfiler.h" #include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h" #include "llvm/Transforms/Instrumentation/InstrProfiling.h" #include "llvm/Transforms/Instrumentation/MemorySanitizer.h" @@ -714,9 +713,8 @@ extern "C" LLVMRustResult LLVMRustOptimize( bool SLPVectorize, bool LoopVectorize, bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers, LLVMRustSanitizerOptions *SanitizerOptions, const char *PGOGenPath, const char *PGOUsePath, bool InstrumentCoverage, - const char *InstrProfileOutput, bool InstrumentGCOV, - const char *PGOSampleUsePath, bool DebugInfoForProfiling, - void *LlvmSelfProfiler, + const char *InstrProfileOutput, const char *PGOSampleUsePath, + bool DebugInfoForProfiling, void *LlvmSelfProfiler, LLVMRustSelfProfileBeforePassCallback BeforePassCallback, LLVMRustSelfProfileAfterPassCallback AfterPassCallback, const char *ExtraPasses, size_t ExtraPassesLen, const char *LLVMPlugins, @@ -847,13 +845,6 @@ extern "C" LLVMRustResult LLVMRustOptimize( }); } - if (InstrumentGCOV) { - PipelineStartEPCallbacks.push_back( - [](ModulePassManager &MPM, OptimizationLevel Level) { - MPM.addPass(GCOVProfilerPass(GCOVOptions::getDefault())); - }); - } - if (InstrumentCoverage) { PipelineStartEPCallbacks.push_back( [InstrProfileOutput](ModulePassManager &MPM, OptimizationLevel Level) { diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 16623915c4019..d2be6ae8d698e 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -778,9 +778,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { fn inject_profiler_runtime(&mut self, krate: &ast::Crate) { if self.sess.opts.unstable_opts.no_profiler_runtime - || !(self.sess.instrument_coverage() - || self.sess.opts.unstable_opts.profile - || self.sess.opts.cg.profile_generate.enabled()) + || !(self.sess.instrument_coverage() || self.sess.opts.cg.profile_generate.enabled()) { return; } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index d733e32f209db..5ee3b4015ebe9 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2453,7 +2453,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let output_types = parse_output_types(early_dcx, &unstable_opts, matches); let mut cg = CodegenOptions::build(early_dcx, matches); - let (disable_local_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto( + let (disable_local_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto( early_dcx, &output_types, matches, @@ -2476,18 +2476,6 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let assert_incr_state = parse_assert_incr_state(early_dcx, &unstable_opts.assert_incr_state); - if unstable_opts.profile && incremental.is_some() { - early_dcx.early_fatal("can't instrument with gcov profiling when compiling incrementally"); - } - if unstable_opts.profile { - match codegen_units { - Some(1) => {} - None => codegen_units = Some(1), - Some(_) => early_dcx - .early_fatal("can't instrument with gcov profiling with multiple codegen units"), - } - } - if cg.profile_generate.enabled() && cg.profile_use.is_some() { early_dcx.early_fatal("options `-C profile-generate` and `-C profile-use` are exclusive"); } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 54a4621db2462..2b158627751bc 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1985,13 +1985,8 @@ options! { proc_macro_execution_strategy: ProcMacroExecutionStrategy = (ProcMacroExecutionStrategy::SameThread, parse_proc_macro_execution_strategy, [UNTRACKED], "how to run proc-macro code (default: same-thread)"), - profile: bool = (false, parse_bool, [TRACKED], - "insert profiling code (default: no)"), profile_closures: bool = (false, parse_no_flag, [UNTRACKED], "profile size of closures"), - profile_emit: Option = (None, parse_opt_pathbuf, [TRACKED], - "file path to emit profiling data at runtime when using 'profile' \ - (default based on relative source path)"), profile_sample_use: Option = (None, parse_opt_pathbuf, [TRACKED], "use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"), profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED], diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index ed091d8fc5710..41da47e9206ca 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -2,12 +2,8 @@ ## Introduction -The Rust compiler includes two code coverage implementations: - -- A GCC-compatible, gcov-based coverage implementation, enabled with `-Z profile`, which derives coverage data based on DebugInfo. -- A source-based code coverage implementation, enabled with `-C instrument-coverage`, which uses LLVM's native, efficient coverage instrumentation to generate very precise coverage data. - -This document describes how to enable and use the LLVM instrumentation-based coverage, via the `-C instrument-coverage` compiler flag. +This document describes how to enable and use LLVM instrumentation-based coverage, +via the `-C instrument-coverage` compiler flag. ## How it works diff --git a/src/doc/unstable-book/src/compiler-flags/profile.md b/src/doc/unstable-book/src/compiler-flags/profile.md deleted file mode 100644 index 71303bfaff20d..0000000000000 --- a/src/doc/unstable-book/src/compiler-flags/profile.md +++ /dev/null @@ -1,27 +0,0 @@ -# `profile` - -The tracking issue for this feature is: [#42524](https://github.com/rust-lang/rust/issues/42524). - ------------------------- - -This feature allows the generation of code coverage reports. - -Set the `-Zprofile` compiler flag in order to enable gcov profiling. - -For example: -```Bash -cargo new testgcov --bin -cd testgcov -export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" -export CARGO_INCREMENTAL=0 -cargo build -cargo run -``` - -Once you've built and run your program, files with the `gcno` (after build) and `gcda` (after execution) extensions will be created. -You can parse them with [llvm-cov gcov](https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-gcov) or [grcov](https://github.com/mozilla/grcov). - -Please note that `RUSTFLAGS` by default applies to everything that cargo builds and runs during a build! -When the `--target` flag is explicitly passed to cargo, the `RUSTFLAGS` no longer apply to build scripts and procedural macros. -For more fine-grained control consider passing a `RUSTC_WRAPPER` program to cargo that only adds the profiling flags to -rustc for the specific crates you want to profile. diff --git a/tests/run-make/profile/rmake.rs b/tests/run-make/profile/rmake.rs deleted file mode 100644 index 58a1b53c0406e..0000000000000 --- a/tests/run-make/profile/rmake.rs +++ /dev/null @@ -1,21 +0,0 @@ -// This test revolves around the rustc flag -Z profile, which should -// generate a .gcno file (initial profiling information) as well -// as a .gcda file (branch counters). The path where these are emitted -// should also be configurable with -Z profile-emit. This test checks -// that the files are produced, and then that the latter flag is respected. -// See https://github.com/rust-lang/rust/pull/42433 - -//@ ignore-cross-compile -//@ needs-profiler-runtime - -use run_make_support::{path, run, rustc}; - -fn main() { - rustc().arg("-g").arg("-Zprofile").input("test.rs").run(); - run("test"); - assert!(path("test.gcno").exists(), "no .gcno file"); - assert!(path("test.gcda").exists(), "no .gcda file"); - rustc().arg("-g").arg("-Zprofile").arg("-Zprofile-emit=abc/abc.gcda").input("test.rs").run(); - run("test"); - assert!(path("abc/abc.gcda").exists(), "gcda file not emitted to defined path"); -} diff --git a/tests/run-make/profile/test.rs b/tests/run-make/profile/test.rs deleted file mode 100644 index f328e4d9d04c3..0000000000000 --- a/tests/run-make/profile/test.rs +++ /dev/null @@ -1 +0,0 @@ -fn main() {} From 588c7a934a05fb8d4aaabf7a42c28f75b749db6c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 31 Oct 2024 04:11:38 +0000 Subject: [PATCH 04/11] nit: stop using TypeckRootCtxt --- compiler/rustc_hir_typeck/src/fallback.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 68776c525551a..963801e57931d 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -14,7 +14,7 @@ use rustc_span::{DUMMY_SP, Span}; use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use tracing::debug; -use crate::{FnCtxt, TypeckRootCtxt, errors}; +use crate::{FnCtxt, errors}; #[derive(Copy, Clone)] pub(crate) enum DivergingFallbackBehavior { @@ -419,7 +419,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { root_vid: ty::TyVid, ) { let unsafe_infer_vars = unsafe_infer_vars.get_or_init(|| { - let unsafe_infer_vars = compute_unsafe_infer_vars(self.root_ctxt, self.body_id); + let unsafe_infer_vars = compute_unsafe_infer_vars(self, self.body_id); debug!(?unsafe_infer_vars); unsafe_infer_vars }); @@ -569,27 +569,26 @@ pub(crate) enum UnsafeUseReason { /// /// `compute_unsafe_infer_vars` will return `{ id(?X) -> (hir_id, span, Call) }` fn compute_unsafe_infer_vars<'a, 'tcx>( - root_ctxt: &'a TypeckRootCtxt<'tcx>, + fcx: &'a FnCtxt<'a, 'tcx>, body_id: LocalDefId, ) -> UnordMap { - let body = - root_ctxt.tcx.hir().maybe_body_owned_by(body_id).expect("body id must have an owner"); + let body = fcx.tcx.hir().maybe_body_owned_by(body_id).expect("body id must have an owner"); let mut res = UnordMap::default(); struct UnsafeInferVarsVisitor<'a, 'tcx> { - root_ctxt: &'a TypeckRootCtxt<'tcx>, + fcx: &'a FnCtxt<'a, 'tcx>, res: &'a mut UnordMap, } impl Visitor<'_> for UnsafeInferVarsVisitor<'_, '_> { fn visit_expr(&mut self, ex: &'_ hir::Expr<'_>) { - let typeck_results = self.root_ctxt.typeck_results.borrow(); + let typeck_results = self.fcx.typeck_results.borrow(); match ex.kind { hir::ExprKind::MethodCall(..) => { if let Some(def_id) = typeck_results.type_dependent_def_id(ex.hir_id) - && let method_ty = self.root_ctxt.tcx.type_of(def_id).instantiate_identity() - && let sig = method_ty.fn_sig(self.root_ctxt.tcx) + && let method_ty = self.fcx.tcx.type_of(def_id).instantiate_identity() + && let sig = method_ty.fn_sig(self.fcx.tcx) && let hir::Safety::Unsafe = sig.safety() { let mut collector = InferVarCollector { @@ -609,7 +608,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>( let func_ty = typeck_results.expr_ty(func); if func_ty.is_fn() - && let sig = func_ty.fn_sig(self.root_ctxt.tcx) + && let sig = func_ty.fn_sig(self.fcx.tcx) && let hir::Safety::Unsafe = sig.safety() { let mut collector = InferVarCollector { @@ -640,7 +639,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>( // If this path refers to an unsafe function, collect inference variables which may affect it. // `is_fn` excludes closures, but those can't be unsafe. if ty.is_fn() - && let sig = ty.fn_sig(self.root_ctxt.tcx) + && let sig = ty.fn_sig(self.fcx.tcx) && let hir::Safety::Unsafe = sig.safety() { let mut collector = InferVarCollector { @@ -698,7 +697,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>( } } - UnsafeInferVarsVisitor { root_ctxt, res: &mut res }.visit_expr(&body.value); + UnsafeInferVarsVisitor { fcx, res: &mut res }.visit_expr(&body.value); debug!(?res, "collected the following unsafe vars for {body_id:?}"); From 41966e71bc386d073124d73cbc34fb279df5f4c8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 31 Oct 2024 04:33:21 +0000 Subject: [PATCH 05/11] Suggest annotations for never type fallback --- compiler/rustc_hir_typeck/src/errors.rs | 49 ++++++++++-- compiler/rustc_hir_typeck/src/fallback.rs | 76 +++++++++++++++++-- .../dependency-on-fallback-to-unit.stderr | 4 + ...-fallback-flowing-into-unsafe.e2015.stderr | 4 + ...-fallback-flowing-into-unsafe.e2024.stderr | 4 + 5 files changed, 125 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index cceaabaff65b6..8fd545939341d 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -169,19 +169,34 @@ pub(crate) struct MissingParenthesesInRange { pub(crate) enum NeverTypeFallbackFlowingIntoUnsafe { #[help] #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_call)] - Call, + Call { + #[subdiagnostic] + sugg: SuggestAnnotations, + }, #[help] #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_method)] - Method, + Method { + #[subdiagnostic] + sugg: SuggestAnnotations, + }, #[help] #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_path)] - Path, + Path { + #[subdiagnostic] + sugg: SuggestAnnotations, + }, #[help] #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_union_field)] - UnionField, + UnionField { + #[subdiagnostic] + sugg: SuggestAnnotations, + }, #[help] #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_deref)] - Deref, + Deref { + #[subdiagnostic] + sugg: SuggestAnnotations, + }, } #[derive(LintDiagnostic)] @@ -191,6 +206,30 @@ pub(crate) struct DependencyOnUnitNeverTypeFallback<'tcx> { #[note] pub obligation_span: Span, pub obligation: ty::Predicate<'tcx>, + #[subdiagnostic] + pub sugg: SuggestAnnotations, +} + +#[derive(Clone)] +pub(crate) struct SuggestAnnotations { + pub suggestion_spans: Vec, +} +impl Subdiagnostic for SuggestAnnotations { + fn add_to_diag_with>( + self, + diag: &mut Diag<'_, G>, + _: &F, + ) { + if self.suggestion_spans.is_empty() { + return; + } + + diag.multipart_suggestion_verbose( + "use `()` annotations to avoid fallback changes", + self.suggestion_spans.into_iter().map(|span| (span, String::from("()"))).collect(), + Applicability::MachineApplicable, + ); + } } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 963801e57931d..02efb12f83561 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -1,5 +1,7 @@ use std::cell::OnceCell; +use std::ops::ControlFlow; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::iterate::DepthFirstSearch; use rustc_data_structures::graph::vec_graph::VecGraph; use rustc_data_structures::graph::{self}; @@ -321,7 +323,11 @@ impl<'tcx> FnCtxt<'_, 'tcx> { let mut diverging_fallback = UnordMap::with_capacity(diverging_vids.len()); let unsafe_infer_vars = OnceCell::new(); - self.lint_obligations_broken_by_never_type_fallback_change(behavior, &diverging_vids); + self.lint_obligations_broken_by_never_type_fallback_change( + behavior, + &diverging_vids, + &coercion_graph, + ); for &diverging_vid in &diverging_vids { let diverging_ty = Ty::new_var(self.tcx, diverging_vid); @@ -429,19 +435,31 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .filter_map(|x| unsafe_infer_vars.get(&x).copied()) .collect::>(); + let sugg = self.try_to_suggest_annotations(&[root_vid], coercion_graph); + for (hir_id, span, reason) in affected_unsafe_infer_vars { self.tcx.emit_node_span_lint( lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, hir_id, span, match reason { - UnsafeUseReason::Call => errors::NeverTypeFallbackFlowingIntoUnsafe::Call, - UnsafeUseReason::Method => errors::NeverTypeFallbackFlowingIntoUnsafe::Method, - UnsafeUseReason::Path => errors::NeverTypeFallbackFlowingIntoUnsafe::Path, + UnsafeUseReason::Call => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Call { sugg: sugg.clone() } + } + UnsafeUseReason::Method => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Method { sugg: sugg.clone() } + } + UnsafeUseReason::Path => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Path { sugg: sugg.clone() } + } UnsafeUseReason::UnionField => { - errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField + errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField { + sugg: sugg.clone(), + } + } + UnsafeUseReason::Deref => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Deref { sugg: sugg.clone() } } - UnsafeUseReason::Deref => errors::NeverTypeFallbackFlowingIntoUnsafe::Deref, }, ); } @@ -451,6 +469,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { &self, behavior: DivergingFallbackBehavior, diverging_vids: &[ty::TyVid], + coercions: &VecGraph, ) { let DivergingFallbackBehavior::ToUnit = behavior else { return }; @@ -478,13 +497,14 @@ impl<'tcx> FnCtxt<'_, 'tcx> { }; // If we have no errors with `fallback = ()`, but *do* have errors with `fallback = !`, - // then this code will be broken by the never type fallback change.qba + // then this code will be broken by the never type fallback change. let unit_errors = remaining_errors_if_fallback_to(self.tcx.types.unit); if unit_errors.is_empty() && let mut never_errors = remaining_errors_if_fallback_to(self.tcx.types.never) && let [ref mut never_error, ..] = never_errors.as_mut_slice() { self.adjust_fulfillment_error_for_expr_obligation(never_error); + let sugg = self.try_to_suggest_annotations(diverging_vids, coercions); self.tcx.emit_node_span_lint( lint::builtin::DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, self.tcx.local_def_id_to_hir_id(self.body_id), @@ -492,6 +512,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { errors::DependencyOnUnitNeverTypeFallback { obligation_span: never_error.obligation.cause.span, obligation: never_error.obligation.predicate, + sugg, }, ) } @@ -541,6 +562,47 @@ impl<'tcx> FnCtxt<'_, 'tcx> { fn root_vid(&self, ty: Ty<'tcx>) -> Option { Some(self.root_var(self.shallow_resolve(ty).ty_vid()?)) } + + fn try_to_suggest_annotations( + &self, + diverging_vids: &[ty::TyVid], + coercions: &VecGraph, + ) -> errors::SuggestAnnotations { + let body = + self.tcx.hir().maybe_body_owned_by(self.body_id).expect("body id must have an owner"); + // For each diverging var, look through the HIR for a place to give it + // a type annotation. We do this per var because we only really need one + // per var. + let suggestion_spans = diverging_vids + .iter() + .copied() + .filter_map(|vid| { + let reachable_vids = + graph::depth_first_search_as_undirected(coercions, vid).collect(); + VidVisitor { reachable_vids, fcx: self }.visit_expr(body.value).break_value() + }) + .collect(); + errors::SuggestAnnotations { suggestion_spans } + } +} + +struct VidVisitor<'a, 'tcx> { + reachable_vids: FxHashSet, + fcx: &'a FnCtxt<'a, 'tcx>, +} +impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { + type Result = ControlFlow; + + fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) -> Self::Result { + if let hir::TyKind::Infer = hir_ty.kind + && let ty = self.fcx.typeck_results.borrow().node_type(hir_ty.hir_id) + && let Some(vid) = self.fcx.root_vid(ty) + && self.reachable_vids.contains(&vid) + { + return ControlFlow::Break(hir_ty.span); + } + hir::intravisit::walk_ty(self, hir_ty) + } } #[derive(Debug, Copy, Clone)] diff --git a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr index ec49137ba7953..065a6e559c15c 100644 --- a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr +++ b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr @@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: Default` will fail LL | false => <_>::default(), | ^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | false => <()>::default(), + | ~~ warning: this function depends on never type fallback being `()` --> $DIR/dependency-on-fallback-to-unit.rs:19:1 diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr index a75039b8237a0..b8de1026e377e 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr @@ -102,6 +102,10 @@ LL | msg_send!(); = note: for more information, see issue #123748 = help: specify the type explicitly = note: this warning originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use `()` annotations to avoid fallback changes + | +LL | match send_message::<() /* ?0 */>() { + | ~~ warning: 10 warnings emitted diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr index 4138e9f8c8622..de44279ff8ccc 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr @@ -102,6 +102,10 @@ LL | msg_send!(); = note: for more information, see issue #123748 = help: specify the type explicitly = note: this error originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) +help: use `()` annotations to avoid fallback changes + | +LL | match send_message::<() /* ?0 */>() { + | ~~ warning: the type `!` does not permit zero-initialization --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:13:18 From ea4fb7c25cbbd21508dea30aa7f29fbdcc1f149d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 31 Oct 2024 15:59:44 +0000 Subject: [PATCH 06/11] Suggest adding self type to method --- compiler/rustc_hir_typeck/src/errors.rs | 25 ++++++++++++++++--- compiler/rustc_hir_typeck/src/fallback.rs | 25 ++++++++++++++++--- .../never-type-fallback-breaking.e2021.stderr | 4 +++ ...ng-fallback-control-flow.nofallback.stderr | 8 ++++++ 4 files changed, 55 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 8fd545939341d..2f09e5e163c15 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -210,9 +210,15 @@ pub(crate) struct DependencyOnUnitNeverTypeFallback<'tcx> { pub sugg: SuggestAnnotations, } +#[derive(Clone)] +pub(crate) enum SuggestAnnotation { + Unit(Span), + Path(Span), +} + #[derive(Clone)] pub(crate) struct SuggestAnnotations { - pub suggestion_spans: Vec, + pub suggestions: Vec, } impl Subdiagnostic for SuggestAnnotations { fn add_to_diag_with>( @@ -220,13 +226,26 @@ impl Subdiagnostic for SuggestAnnotations { diag: &mut Diag<'_, G>, _: &F, ) { - if self.suggestion_spans.is_empty() { + if self.suggestions.is_empty() { return; } + let mut suggestions = vec![]; + for suggestion in self.suggestions { + match suggestion { + SuggestAnnotation::Unit(span) => { + suggestions.push((span, "()".to_string())); + } + SuggestAnnotation::Path(span) => { + suggestions.push((span.shrink_to_lo(), "<() as ".to_string())); + suggestions.push((span.shrink_to_hi(), ">".to_string())); + } + } + } + diag.multipart_suggestion_verbose( "use `()` annotations to avoid fallback changes", - self.suggestion_spans.into_iter().map(|span| (span, String::from("()"))).collect(), + suggestions, Applicability::MachineApplicable, ); } diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 02efb12f83561..d6be237acf4d6 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -8,6 +8,7 @@ use rustc_data_structures::graph::{self}; use rustc_data_structures::unord::{UnordBag, UnordMap, UnordSet}; use rustc_hir as hir; use rustc_hir::HirId; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::Visitor; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; use rustc_session::lint; @@ -573,7 +574,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // For each diverging var, look through the HIR for a place to give it // a type annotation. We do this per var because we only really need one // per var. - let suggestion_spans = diverging_vids + let suggestions = diverging_vids .iter() .copied() .filter_map(|vid| { @@ -582,16 +583,17 @@ impl<'tcx> FnCtxt<'_, 'tcx> { VidVisitor { reachable_vids, fcx: self }.visit_expr(body.value).break_value() }) .collect(); - errors::SuggestAnnotations { suggestion_spans } + errors::SuggestAnnotations { suggestions } } } +/// Try to collect a useful suggestion to preserve fallback to `()`. struct VidVisitor<'a, 'tcx> { reachable_vids: FxHashSet, fcx: &'a FnCtxt<'a, 'tcx>, } impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { - type Result = ControlFlow; + type Result = ControlFlow; fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) -> Self::Result { if let hir::TyKind::Infer = hir_ty.kind @@ -599,10 +601,25 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { && let Some(vid) = self.fcx.root_vid(ty) && self.reachable_vids.contains(&vid) { - return ControlFlow::Break(hir_ty.span); + return ControlFlow::Break(errors::SuggestAnnotation::Unit(hir_ty.span)); } hir::intravisit::walk_ty(self, hir_ty) } + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result { + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let Res::Def(DefKind::AssocFn, def_id) = path.res + && self.fcx.tcx.trait_of_item(def_id).is_some() + && let self_ty = self.fcx.typeck_results.borrow().node_args(expr.hir_id).type_at(0) + && let Some(vid) = self.fcx.root_vid(self_ty) + && self.reachable_vids.contains(&vid) + && let [.., trait_segment, _method_segment] = path.segments + { + let span = path.span.shrink_to_lo().to(trait_segment.ident.span); + return ControlFlow::Break(errors::SuggestAnnotation::Path(span)); + } + hir::intravisit::walk_expr(self, expr) + } } #[derive(Debug, Copy, Clone)] diff --git a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr index 134fd098b7e4a..703f3d6266cf6 100644 --- a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr +++ b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr @@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: Default` will fail LL | true => Default::default(), | ^^^^^^^^^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | true => <() as Default>::default(), + | ++++++ + warning: this function depends on never type fallback being `()` --> $DIR/never-type-fallback-breaking.rs:27:1 diff --git a/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr index 2a3c5edc21847..dee112e245a76 100644 --- a/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr +++ b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr @@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: UnitDefault` will fail LL | x = UnitDefault::default(); | ^^^^^^^^^^^^^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | x = <() as UnitDefault>::default(); + | ++++++ + warning: this function depends on never type fallback being `()` --> $DIR/diverging-fallback-control-flow.rs:42:1 @@ -28,6 +32,10 @@ note: in edition 2024, the requirement `!: UnitDefault` will fail | LL | x = UnitDefault::default(); | ^^^^^^^^^^^^^^^^^^^^^^ +help: use `()` annotations to avoid fallback changes + | +LL | x = <() as UnitDefault>::default(); + | ++++++ + warning: 2 warnings emitted From c930bba2836c1daa4ffa72ce3ec4d3e570ce188e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 31 Oct 2024 16:05:42 +0000 Subject: [PATCH 07/11] And locals too --- compiler/rustc_hir_typeck/src/errors.rs | 4 ++++ compiler/rustc_hir_typeck/src/fallback.rs | 13 +++++++++++++ .../never-type-fallback-breaking.e2021.stderr | 4 ++-- .../defaulted-never-note.nofallback.stderr | 4 ++++ ...iverging-fallback-control-flow.nofallback.stderr | 8 ++++---- ...-fallback-unconstrained-return.nofallback.stderr | 4 ++++ 6 files changed, 31 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 2f09e5e163c15..fdeeed92565c3 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -214,6 +214,7 @@ pub(crate) struct DependencyOnUnitNeverTypeFallback<'tcx> { pub(crate) enum SuggestAnnotation { Unit(Span), Path(Span), + Local(Span), } #[derive(Clone)] @@ -240,6 +241,9 @@ impl Subdiagnostic for SuggestAnnotations { suggestions.push((span.shrink_to_lo(), "<() as ".to_string())); suggestions.push((span.shrink_to_hi(), ">".to_string())); } + SuggestAnnotation::Local(span) => { + suggestions.push((span, ": ()".to_string())); + } } } diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index d6be237acf4d6..cefc28b400f6e 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -620,6 +620,19 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { } hir::intravisit::walk_expr(self, expr) } + + fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) -> Self::Result { + if let None = local.ty + && let ty = self.fcx.typeck_results.borrow().node_type(local.hir_id) + && let Some(vid) = self.fcx.root_vid(ty) + && self.reachable_vids.contains(&vid) + { + return ControlFlow::Break(errors::SuggestAnnotation::Local( + local.pat.span.shrink_to_hi(), + )); + } + hir::intravisit::walk_local(self, local) + } } #[derive(Debug, Copy, Clone)] diff --git a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr index 703f3d6266cf6..1c6685261fb3e 100644 --- a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr +++ b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr @@ -15,8 +15,8 @@ LL | true => Default::default(), = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default help: use `()` annotations to avoid fallback changes | -LL | true => <() as Default>::default(), - | ++++++ + +LL | let x: () = match true { + | ++++ warning: this function depends on never type fallback being `()` --> $DIR/never-type-fallback-breaking.rs:27:1 diff --git a/tests/ui/never_type/defaulted-never-note.nofallback.stderr b/tests/ui/never_type/defaulted-never-note.nofallback.stderr index d88615186dd63..6bc4501b6a375 100644 --- a/tests/ui/never_type/defaulted-never-note.nofallback.stderr +++ b/tests/ui/never_type/defaulted-never-note.nofallback.stderr @@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: ImplementedForUnitButNotNever` will f LL | foo(_x); | ^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | let _x: () = return; + | ++++ warning: 1 warning emitted diff --git a/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr index dee112e245a76..d40d1da76f9ac 100644 --- a/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr +++ b/tests/ui/never_type/diverging-fallback-control-flow.nofallback.stderr @@ -15,8 +15,8 @@ LL | x = UnitDefault::default(); = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default help: use `()` annotations to avoid fallback changes | -LL | x = <() as UnitDefault>::default(); - | ++++++ + +LL | let x: (); + | ++++ warning: this function depends on never type fallback being `()` --> $DIR/diverging-fallback-control-flow.rs:42:1 @@ -34,8 +34,8 @@ LL | x = UnitDefault::default(); | ^^^^^^^^^^^^^^^^^^^^^^ help: use `()` annotations to avoid fallback changes | -LL | x = <() as UnitDefault>::default(); - | ++++++ + +LL | let x: (); + | ++++ warning: 2 warnings emitted diff --git a/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr b/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr index b485c94df4d6f..30a5e60a75848 100644 --- a/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr +++ b/tests/ui/never_type/diverging-fallback-unconstrained-return.nofallback.stderr @@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: UnitReturn` will fail LL | let _ = if true { unconstrained_return() } else { panic!() }; | ^^^^^^^^^^^^^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | let _: () = if true { unconstrained_return() } else { panic!() }; + | ++++ warning: 1 warning emitted From df6f5841e5fcceb2c9ff43035dcede4f35450f5a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 31 Oct 2024 16:58:28 +0000 Subject: [PATCH 08/11] And also suggest for qpaths --- compiler/rustc_hir_typeck/src/errors.rs | 11 +++ compiler/rustc_hir_typeck/src/fallback.rs | 68 +++++++++++++++++++ .../never-type-fallback-breaking.e2021.stderr | 4 ++ .../dependency-on-fallback-to-unit.stderr | 4 ++ ...verging-fallback-no-leak.nofallback.stderr | 4 ++ .../fallback-closure-ret.nofallback.stderr | 4 ++ ...-fallback-flowing-into-unsafe.e2015.stderr | 28 ++++++++ ...-fallback-flowing-into-unsafe.e2024.stderr | 28 ++++++++ 8 files changed, 151 insertions(+) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index fdeeed92565c3..4f579b05d83b6 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -215,6 +215,7 @@ pub(crate) enum SuggestAnnotation { Unit(Span), Path(Span), Local(Span), + Turbo(Span, usize, usize), } #[derive(Clone)] @@ -244,6 +245,16 @@ impl Subdiagnostic for SuggestAnnotations { SuggestAnnotation::Local(span) => { suggestions.push((span, ": ()".to_string())); } + SuggestAnnotation::Turbo(span, n_args, idx) => suggestions.push(( + span, + format!( + "::<{}>", + (0..n_args) + .map(|i| if i == idx { "()" } else { "_" }) + .collect::>() + .join(", "), + ), + )), } } diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index cefc28b400f6e..ada716addd02d 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -9,6 +9,7 @@ use rustc_data_structures::unord::{UnordBag, UnordMap, UnordSet}; use rustc_hir as hir; use rustc_hir::HirId; use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; use rustc_session::lint; @@ -592,10 +593,45 @@ struct VidVisitor<'a, 'tcx> { reachable_vids: FxHashSet, fcx: &'a FnCtxt<'a, 'tcx>, } +impl<'tcx> VidVisitor<'_, 'tcx> { + fn suggest_for_segment( + &self, + arg_segment: &'tcx hir::PathSegment<'tcx>, + def_id: DefId, + id: HirId, + ) -> ControlFlow { + if arg_segment.args.is_none() + && let Some(all_args) = self.fcx.typeck_results.borrow().node_args_opt(id) + && let generics = self.fcx.tcx.generics_of(def_id) + && let args = &all_args[generics.parent_count..] + // We can't turbofish consts :( + && args.iter().all(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_) | ty::GenericArgKind::Lifetime(_))) + { + let n_tys = args + .iter() + .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_))) + .count(); + for (idx, arg) in args.iter().enumerate() { + if let Some(ty) = arg.as_type() + && let Some(vid) = self.fcx.root_vid(ty) + && self.reachable_vids.contains(&vid) + { + return ControlFlow::Break(errors::SuggestAnnotation::Turbo( + arg_segment.ident.span.shrink_to_hi(), + n_tys, + idx, + )); + } + } + } + ControlFlow::Continue(()) + } +} impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { type Result = ControlFlow; fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) -> Self::Result { + // Try to replace `_` with `()`. if let hir::TyKind::Infer = hir_ty.kind && let ty = self.fcx.typeck_results.borrow().node_type(hir_ty.hir_id) && let Some(vid) = self.fcx.root_vid(ty) @@ -606,7 +642,32 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { hir::intravisit::walk_ty(self, hir_ty) } + fn visit_qpath( + &mut self, + qpath: &'tcx rustc_hir::QPath<'tcx>, + id: HirId, + _span: Span, + ) -> Self::Result { + let arg_segment = match qpath { + hir::QPath::Resolved(_, path) => { + path.segments.last().expect("paths should have a segment") + } + hir::QPath::TypeRelative(_, segment) => segment, + hir::QPath::LangItem(..) => { + return hir::intravisit::walk_qpath(self, qpath, id); + } + }; + // Alternatively, try to turbofish `::<_, (), _>` (ignoring lifetimes, + // since we don't need to turbofish those; they'll be inferred). + // FIXME: Same logic could work for types... + if let Some(def_id) = self.fcx.typeck_results.borrow().qpath_res(qpath, id).opt_def_id() { + self.suggest_for_segment(arg_segment, def_id, id)?; + } + hir::intravisit::walk_qpath(self, qpath, id) + } + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result { + // Try to suggest adding an explicit qself `()` to a trait method path. if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let Res::Def(DefKind::AssocFn, def_id) = path.res && self.fcx.tcx.trait_of_item(def_id).is_some() @@ -618,6 +679,13 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { let span = path.span.shrink_to_lo().to(trait_segment.ident.span); return ControlFlow::Break(errors::SuggestAnnotation::Path(span)); } + // Or else turbofishing the method + if let hir::ExprKind::MethodCall(segment, ..) = expr.kind + && let Some(def_id) = + self.fcx.typeck_results.borrow().type_dependent_def_id(expr.hir_id) + { + self.suggest_for_segment(segment, def_id, expr.hir_id)?; + } hir::intravisit::walk_expr(self, expr) } diff --git a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr index 1c6685261fb3e..79eee2a3defd0 100644 --- a/tests/ui/editions/never-type-fallback-breaking.e2021.stderr +++ b/tests/ui/editions/never-type-fallback-breaking.e2021.stderr @@ -32,6 +32,10 @@ note: in edition 2024, the requirement `!: Default` will fail | LL | deserialize()?; | ^^^^^^^^^^^^^ +help: use `()` annotations to avoid fallback changes + | +LL | deserialize::<()>()?; + | ++++++ warning: 2 warnings emitted diff --git a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr index 065a6e559c15c..79f47bb5fbc11 100644 --- a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr +++ b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr @@ -32,6 +32,10 @@ note: in edition 2024, the requirement `!: Default` will fail | LL | deserialize()?; | ^^^^^^^^^^^^^ +help: use `()` annotations to avoid fallback changes + | +LL | deserialize::<()>()?; + | ++++++ warning: 2 warnings emitted diff --git a/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr b/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr index 11245cc7aabf9..d11c21d9573be 100644 --- a/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr +++ b/tests/ui/never_type/diverging-fallback-no-leak.nofallback.stderr @@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: Test` will fail LL | unconstrained_arg(return); | ^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | unconstrained_arg::<()>(return); + | ++++++ warning: 1 warning emitted diff --git a/tests/ui/never_type/fallback-closure-ret.nofallback.stderr b/tests/ui/never_type/fallback-closure-ret.nofallback.stderr index 3fb5536dee7e3..fb0166dd9e06e 100644 --- a/tests/ui/never_type/fallback-closure-ret.nofallback.stderr +++ b/tests/ui/never_type/fallback-closure-ret.nofallback.stderr @@ -13,6 +13,10 @@ note: in edition 2024, the requirement `!: Bar` will fail LL | foo(|| panic!()); | ^^^^^^^^^^^^^^^^ = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | foo::<(), _>(|| panic!()); + | +++++++++ warning: 1 warning emitted diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr index b8de1026e377e..6a48a7b9b47af 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr @@ -8,6 +8,10 @@ LL | unsafe { mem::zeroed() } = note: for more information, see issue #123748 = help: specify the type explicitly = note: `#[warn(never_type_fallback_flowing_into_unsafe)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | unsafe { mem::zeroed::<()>() } + | ++++++ warning: never type fallback affects this call to an `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:30:13 @@ -18,6 +22,10 @@ LL | core::mem::transmute(Zst) = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | core::mem::transmute::<_, ()>(Zst) + | +++++++++ warning: never type fallback affects this union access --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:47:18 @@ -38,6 +46,10 @@ LL | unsafe { *ptr::from_ref(&()).cast() } = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | unsafe { *ptr::from_ref(&()).cast::<()>() } + | ++++++ warning: never type fallback affects this call to an `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:79:18 @@ -48,6 +60,10 @@ LL | unsafe { internally_create(x) } = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | unsafe { internally_create::<()>(x) } + | ++++++ warning: never type fallback affects this call to an `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:97:18 @@ -58,6 +74,10 @@ LL | unsafe { zeroed() } = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | let zeroed = mem::zeroed::<()>; + | ++++++ warning: never type fallback affects this `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:92:22 @@ -68,6 +88,10 @@ LL | let zeroed = mem::zeroed; = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | let zeroed = mem::zeroed::<()>; + | ++++++ warning: never type fallback affects this `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:115:17 @@ -78,6 +102,10 @@ LL | let f = internally_create; = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | let f = internally_create::<()>; + | ++++++ warning: never type fallback affects this call to an `unsafe` method --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:140:13 diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr index de44279ff8ccc..844cd62c267b5 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr @@ -8,6 +8,10 @@ LL | unsafe { mem::zeroed() } = note: for more information, see issue #123748 = help: specify the type explicitly = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` on by default +help: use `()` annotations to avoid fallback changes + | +LL | unsafe { mem::zeroed::<()>() } + | ++++++ error: never type fallback affects this call to an `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:30:13 @@ -18,6 +22,10 @@ LL | core::mem::transmute(Zst) = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | core::mem::transmute::<_, ()>(Zst) + | +++++++++ error: never type fallback affects this union access --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:47:18 @@ -38,6 +46,10 @@ LL | unsafe { *ptr::from_ref(&()).cast() } = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | unsafe { *ptr::from_ref(&()).cast::<()>() } + | ++++++ error: never type fallback affects this call to an `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:79:18 @@ -48,6 +60,10 @@ LL | unsafe { internally_create(x) } = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | unsafe { internally_create::<()>(x) } + | ++++++ error: never type fallback affects this call to an `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:97:18 @@ -58,6 +74,10 @@ LL | unsafe { zeroed() } = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | let zeroed = mem::zeroed::<()>; + | ++++++ error: never type fallback affects this `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:92:22 @@ -68,6 +88,10 @@ LL | let zeroed = mem::zeroed; = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | let zeroed = mem::zeroed::<()>; + | ++++++ error: never type fallback affects this `unsafe` function --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:115:17 @@ -78,6 +102,10 @@ LL | let f = internally_create; = warning: this will change its meaning in a future release! = note: for more information, see issue #123748 = help: specify the type explicitly +help: use `()` annotations to avoid fallback changes + | +LL | let f = internally_create::<()>; + | ++++++ error: never type fallback affects this call to an `unsafe` method --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:140:13 From b4248aeec366d31efe595eb5f671f589c849d6a3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 1 Nov 2024 01:46:17 +0000 Subject: [PATCH 09/11] nits --- compiler/rustc_hir_typeck/src/fallback.rs | 30 +++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index ada716addd02d..8d8573c65c5b8 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -565,6 +565,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> { Some(self.root_var(self.shallow_resolve(ty).ty_vid()?)) } + /// Given a set of diverging vids and coercions, walk the HIR to gather a + /// set of suggestions which can be applied to preserve fallback to unit. fn try_to_suggest_annotations( &self, diverging_vids: &[ty::TyVid], @@ -574,26 +576,34 @@ impl<'tcx> FnCtxt<'_, 'tcx> { self.tcx.hir().maybe_body_owned_by(self.body_id).expect("body id must have an owner"); // For each diverging var, look through the HIR for a place to give it // a type annotation. We do this per var because we only really need one - // per var. + // suggestion to influence a var to be `()`. let suggestions = diverging_vids .iter() .copied() .filter_map(|vid| { let reachable_vids = graph::depth_first_search_as_undirected(coercions, vid).collect(); - VidVisitor { reachable_vids, fcx: self }.visit_expr(body.value).break_value() + AnnotateUnitFallbackVisitor { reachable_vids, fcx: self } + .visit_expr(body.value) + .break_value() }) .collect(); errors::SuggestAnnotations { suggestions } } } -/// Try to collect a useful suggestion to preserve fallback to `()`. -struct VidVisitor<'a, 'tcx> { +/// Try to walk the HIR to find a place to insert a useful suggestion +/// to preserve fallback to `()` in 2024. +struct AnnotateUnitFallbackVisitor<'a, 'tcx> { reachable_vids: FxHashSet, fcx: &'a FnCtxt<'a, 'tcx>, } -impl<'tcx> VidVisitor<'_, 'tcx> { +impl<'tcx> AnnotateUnitFallbackVisitor<'_, 'tcx> { + // For a given path segment, if it's missing a turbofish, try to suggest adding + // one so we can constrain an argument to `()`. To keep the suggestion simple, + // we want to simply suggest `_` for all the other args. This (for now) only + // works when there are only type variables (and region variables, since we can + // elide them)... fn suggest_for_segment( &self, arg_segment: &'tcx hir::PathSegment<'tcx>, @@ -627,7 +637,7 @@ impl<'tcx> VidVisitor<'_, 'tcx> { ControlFlow::Continue(()) } } -impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { +impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> { type Result = ControlFlow; fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) -> Self::Result { @@ -657,9 +667,7 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { return hir::intravisit::walk_qpath(self, qpath, id); } }; - // Alternatively, try to turbofish `::<_, (), _>` (ignoring lifetimes, - // since we don't need to turbofish those; they'll be inferred). - // FIXME: Same logic could work for types... + // Alternatively, try to turbofish `::<_, (), _>`. if let Some(def_id) = self.fcx.typeck_results.borrow().qpath_res(qpath, id).opt_def_id() { self.suggest_for_segment(arg_segment, def_id, id)?; } @@ -668,6 +676,7 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result { // Try to suggest adding an explicit qself `()` to a trait method path. + // i.e. changing `Default::default()` to `<() as Default>::default()`. if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let Res::Def(DefKind::AssocFn, def_id) = path.res && self.fcx.tcx.trait_of_item(def_id).is_some() @@ -679,7 +688,7 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, 'tcx> { let span = path.span.shrink_to_lo().to(trait_segment.ident.span); return ControlFlow::Break(errors::SuggestAnnotation::Path(span)); } - // Or else turbofishing the method + // Or else, try suggesting turbofishing the method args. if let hir::ExprKind::MethodCall(segment, ..) = expr.kind && let Some(def_id) = self.fcx.typeck_results.borrow().type_dependent_def_id(expr.hir_id) @@ -690,6 +699,7 @@ impl<'tcx> Visitor<'tcx> for VidVisitor<'_, '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 && let ty = self.fcx.typeck_results.borrow().node_type(local.hir_id) && let Some(vid) = self.fcx.root_vid(ty) From f3f159315d70b223e1aa1eedf5d93fb6d0c6da94 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 1 Nov 2024 12:47:53 +1100 Subject: [PATCH 10/11] coverage: Regression test for inlining into an uninstrumented crate --- tests/coverage/auxiliary/inline_mixed_helper.rs | 13 +++++++++++++ tests/coverage/inline_mixed.coverage | 14 ++++++++++++++ tests/coverage/inline_mixed.rs | 14 ++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 tests/coverage/auxiliary/inline_mixed_helper.rs create mode 100644 tests/coverage/inline_mixed.coverage create mode 100644 tests/coverage/inline_mixed.rs diff --git a/tests/coverage/auxiliary/inline_mixed_helper.rs b/tests/coverage/auxiliary/inline_mixed_helper.rs new file mode 100644 index 0000000000000..1e91ab8ce7c03 --- /dev/null +++ b/tests/coverage/auxiliary/inline_mixed_helper.rs @@ -0,0 +1,13 @@ +//@ edition: 2021 +//@ compile-flags: -Cinstrument-coverage=on + +#[inline] +pub fn inline_me() {} + +#[inline(never)] +pub fn no_inlining_please() {} + +pub fn generic() {} + +// FIXME(#132436): Even though this doesn't ICE, it still produces coverage +// reports that undercount the affected code. diff --git a/tests/coverage/inline_mixed.coverage b/tests/coverage/inline_mixed.coverage new file mode 100644 index 0000000000000..5012dc7e6c3f2 --- /dev/null +++ b/tests/coverage/inline_mixed.coverage @@ -0,0 +1,14 @@ + LL| |//@ edition: 2021 + LL| |//@ compile-flags: -Cinstrument-coverage=on + LL| | + LL| |#[inline] + LL| 0|pub fn inline_me() {} + LL| | + LL| |#[inline(never)] + LL| 1|pub fn no_inlining_please() {} + LL| | + LL| 0|pub fn generic() {} + LL| | + LL| |// FIXME(#132436): Even though this doesn't ICE, it still produces coverage + LL| |// reports that undercount the affected code. + diff --git a/tests/coverage/inline_mixed.rs b/tests/coverage/inline_mixed.rs new file mode 100644 index 0000000000000..caf924c80069c --- /dev/null +++ b/tests/coverage/inline_mixed.rs @@ -0,0 +1,14 @@ +//@ edition: 2021 +//@ compile-flags: -Cinstrument-coverage=off +//@ aux-crate: inline_mixed_helper=inline_mixed_helper.rs + +// Regression test for . +// Various forms of cross-crate inlining can cause coverage statements to be +// inlined into crates that are being built without coverage instrumentation. +// At the very least, we need to not ICE when that happens. + +fn main() { + inline_mixed_helper::inline_me(); + inline_mixed_helper::no_inlining_please(); + inline_mixed_helper::generic::(); +} From b6a49d8969de0f15406815761bcaffda8ce6b797 Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 1 Nov 2024 10:15:08 +0800 Subject: [PATCH 11/11] Remove unncessary option for default rust-analyzer setting --- src/bootstrap/src/core/build_steps/setup.rs | 1 + src/etc/rust_analyzer_settings.json | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 519649779336c..704fa46ab1ee2 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -571,6 +571,7 @@ Select which editor you would like to set up [default: None]: "; "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a", "828666b021d837a33e78d870b56d34c88a5e2c85de58b693607ec574f0c27000", "811fb3b063c739d261fd8590dd30242e117908f5a095d594fa04585daa18ec4d", + "4eecb58a2168b252077369da446c30ed0e658301efe69691979d1ef0443928f4", ], EditorKind::Emacs => vec![ "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0", diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json index a20105f0ef378..d1b186fd316d7 100644 --- a/src/etc/rust_analyzer_settings.json +++ b/src/etc/rust_analyzer_settings.json @@ -1,6 +1,5 @@ { "git.detectSubmodulesLimit": 20, - "rust-analyzer.check.invocationLocation": "root", "rust-analyzer.check.invocationStrategy": "once", "rust-analyzer.check.overrideCommand": [ "python3", @@ -24,7 +23,6 @@ "rust-analyzer.procMacro.server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv", "rust-analyzer.procMacro.enable": true, "rust-analyzer.cargo.buildScripts.enable": true, - "rust-analyzer.cargo.buildScripts.invocationLocation": "root", "rust-analyzer.cargo.buildScripts.invocationStrategy": "once", "rust-analyzer.cargo.buildScripts.overrideCommand": [ "python3",