From ac661f64f089f6c2d667b6484edf3c1322f8fe0c Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 21 Oct 2022 10:09:11 -0500 Subject: [PATCH 1/4] Add -Zborrowck-unreachable=no The magic button for when you don't care about whether your code works, you just want it to compile. Currently, this avoids running borrowck or typeck on dead code that isn't monomorphized. In the future, it could be extended to ignore (but still emit) parse errors, mismatches in generic parameters, uses of private items, as long as the use doesn't need to make it to codegen. However, I don't propose to put them under the same flag. There is precedent for this in Haskell as -f defer-type-errors: https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/defer_type_errors.html Build systems also commonly have this as --keep-going, which ignores failed builds while continuing to run other builds that aren't dependent on the one that failed. Some example motivations: - When making a large refactor, run a single test that doesn't affect the rest of the codebase. In essence this is a "poor man's workspace", but adhoc and much faster than splitting out a single crate into multiple. - Prototype a new application where you're rapidly changing the code and don't need it to be precisely correct, just see if it could eventually be made to compile. --- compiler/rustc_driver/src/lib.rs | 7 +++++++ compiler/rustc_interface/src/queries.rs | 4 +++- compiler/rustc_monomorphize/src/collector.rs | 10 ++++++++++ compiler/rustc_session/src/options.rs | 2 ++ src/test/ui/fuckit/borrowck-live-code.rs | 9 +++++++++ src/test/ui/fuckit/borrowck-live-code.stderr | 10 ++++++++++ src/test/ui/fuckit/no-borrowck-dead-code.rs | 10 ++++++++++ 7 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/fuckit/borrowck-live-code.rs create mode 100644 src/test/ui/fuckit/borrowck-live-code.stderr create mode 100644 src/test/ui/fuckit/no-borrowck-dead-code.rs diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 8e176efb2a9ed..0ce5fc5807614 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -367,6 +367,13 @@ fn run_compiler( return early_exit(); } + // We need to get at least far before we have even a hope of generating a binary. + // FIXME: is that true? maybe we can ignore unresolved locals in dead code and that sort of thing + // That might require making name-resolution incremental, though. + if !sess.opts.unstable_opts.borrowck_unreachable { + return queries.linker().map(Some); + } + queries.global_ctxt()?; if sess.opts.unstable_opts.no_analysis { diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index fc0b11183f7bf..4530fcab28c50 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -237,7 +237,9 @@ impl<'tcx> Queries<'tcx> { self.ongoing_codegen.compute(|| { let outputs = self.prepare_outputs()?; self.global_ctxt()?.peek_mut().enter(|tcx| { - tcx.analysis(()).ok(); + if tcx.sess.opts.unstable_opts.borrowck_unreachable { + tcx.analysis(()).ok(); + } // Don't do code generation if there were any errors self.session().compile_status()?; diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 559ce227454de..a181e205e0397 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1186,6 +1186,16 @@ struct RootCollector<'a, 'tcx> { impl<'v> RootCollector<'_, 'v> { fn process_item(&mut self, id: hir::ItemId) { + let def_id = id.owner_id.def_id; + let (live_symbols, _) = self.tcx.live_symbols_and_ignored_derived_traits(()); + if !live_symbols.contains(&def_id) { + // This is dead code; ignore it. + return; + } + // We need this for `-Zborrowck-unreachable=no`, since we don't borrowck the whole crate at once, only on-demand. + if self.tcx.hir().maybe_body_owned_by(def_id).is_some() { + self.tcx.ensure().mir_borrowck(def_id); + } match self.tcx.def_kind(id.owner_id) { DefKind::Enum | DefKind::Struct | DefKind::Union => { let item = self.tcx.hir().item(id); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index f9ee202466f67..433946117e36a 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1311,6 +1311,8 @@ options! { (default: no)"), force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED], "force all crates to be `rustc_private` unstable (default: no)"), + borrowck_unreachable: bool = (true, parse_bool, [TRACKED], + "force borrowck to run even on functions that are never used"), fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], "set the optimization fuel quota for a crate"), function_sections: Option = (None, parse_opt_bool, [TRACKED], diff --git a/src/test/ui/fuckit/borrowck-live-code.rs b/src/test/ui/fuckit/borrowck-live-code.rs new file mode 100644 index 0000000000000..6628017cc8f4f --- /dev/null +++ b/src/test/ui/fuckit/borrowck-live-code.rs @@ -0,0 +1,9 @@ +// compile-flags: -Z borrowck-unreachable=no +// build-fail +fn live_code(s: &str) -> &'static str { + s //~ ERROR lifetime may not live long enough +} + +fn main() { + println!("{}", live_code("he he he")); +} diff --git a/src/test/ui/fuckit/borrowck-live-code.stderr b/src/test/ui/fuckit/borrowck-live-code.stderr new file mode 100644 index 0000000000000..2659cc0decdc1 --- /dev/null +++ b/src/test/ui/fuckit/borrowck-live-code.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/borrowck-live-code.rs:4:5 + | +LL | fn live_code(s: &str) -> &'static str { + | - let's call the lifetime of this reference `'1` +LL | s + | ^ returning this value requires that `'1` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/fuckit/no-borrowck-dead-code.rs b/src/test/ui/fuckit/no-borrowck-dead-code.rs new file mode 100644 index 0000000000000..3f28416691e7f --- /dev/null +++ b/src/test/ui/fuckit/no-borrowck-dead-code.rs @@ -0,0 +1,10 @@ +// compile-flags: -Z borrowck-unreachable=no +// run-pass + +fn dead_code(s: &str) -> &'static str { + s +} + +fn main() { + println!("he he he") +} From 62bfb737d74912d422fbad7be20ac4b16bed6391 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 28 Nov 2022 15:57:13 -0500 Subject: [PATCH 2/4] use a less brute-force hammer in `process_item` not all processed items will actually be instantiated; wait to borrowck them until it actually happens --- compiler/rustc_middle/src/mir/mono.rs | 7 +++++++ compiler/rustc_monomorphize/src/collector.rs | 10 ---------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 15a24aa4ace51..ba498c5c6242d 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -175,6 +175,13 @@ impl<'tcx> MonoItem<'tcx> { MonoItem::GlobalAsm(..) => return true, }; + // We need this for `-Zborrowck-unreachable=no`, since we don't borrowck the whole crate at once, only on-demand. + if let Some(local) = def_id.as_local() { + if tcx.hir().maybe_body_owned_by(local).is_some() { + tcx.ensure().mir_borrowck(local); + } + } + !tcx.subst_and_check_impossible_predicates((def_id, &substs)) } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index a181e205e0397..559ce227454de 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1186,16 +1186,6 @@ struct RootCollector<'a, 'tcx> { impl<'v> RootCollector<'_, 'v> { fn process_item(&mut self, id: hir::ItemId) { - let def_id = id.owner_id.def_id; - let (live_symbols, _) = self.tcx.live_symbols_and_ignored_derived_traits(()); - if !live_symbols.contains(&def_id) { - // This is dead code; ignore it. - return; - } - // We need this for `-Zborrowck-unreachable=no`, since we don't borrowck the whole crate at once, only on-demand. - if self.tcx.hir().maybe_body_owned_by(def_id).is_some() { - self.tcx.ensure().mir_borrowck(def_id); - } match self.tcx.def_kind(id.owner_id) { DefKind::Enum | DefKind::Struct | DefKind::Union => { let item = self.tcx.hir().item(id); From 8628ddfc7c8f3bc3be09ded8b5ef465a5d214a2d Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 28 Nov 2022 16:03:22 -0500 Subject: [PATCH 3/4] fix tidy --- compiler/rustc_session/src/options.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 433946117e36a..7cf0351b6b77f 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1225,6 +1225,8 @@ options! { binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \ (default: no)"), + borrowck_unreachable: bool = (true, parse_bool, [TRACKED], + "force borrowck to run even on functions that are never used"), box_noalias: Option = (None, parse_opt_bool, [TRACKED], "emit noalias metadata for box (default: yes)"), branch_protection: Option = (None, parse_branch_protection, [TRACKED], @@ -1311,8 +1313,6 @@ options! { (default: no)"), force_unstable_if_unmarked: bool = (false, parse_bool, [TRACKED], "force all crates to be `rustc_private` unstable (default: no)"), - borrowck_unreachable: bool = (true, parse_bool, [TRACKED], - "force borrowck to run even on functions that are never used"), fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], "set the optimization fuel quota for a crate"), function_sections: Option = (None, parse_opt_bool, [TRACKED], From b3d7774ba4af4037afe9da8ebdc457aca4919256 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 28 Nov 2022 16:33:29 -0500 Subject: [PATCH 4/4] make sure `dead_code` warnings are shown --- compiler/rustc_driver/src/lib.rs | 6 ++++++ compiler/rustc_interface/src/queries.rs | 2 +- src/test/ui/fuckit/no-borrowck-dead-code.rs | 2 +- src/test/ui/fuckit/no-borrowck-dead-code.stderr | 10 ++++++++++ src/test/ui/fuckit/no-typeck-dead-code.rs | 10 ++++++++++ src/test/ui/fuckit/no-typeck-dead-code.stderr | 10 ++++++++++ src/test/ui/fuckit/typeck-live-code.rs | 8 ++++++++ src/test/ui/fuckit/typeck-live-code.stderr | 11 +++++++++++ 8 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/fuckit/no-borrowck-dead-code.stderr create mode 100644 src/test/ui/fuckit/no-typeck-dead-code.rs create mode 100644 src/test/ui/fuckit/no-typeck-dead-code.stderr create mode 100644 src/test/ui/fuckit/typeck-live-code.rs create mode 100644 src/test/ui/fuckit/typeck-live-code.stderr diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 0ce5fc5807614..d59c91318b4e7 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -371,6 +371,12 @@ fn run_compiler( // FIXME: is that true? maybe we can ignore unresolved locals in dead code and that sort of thing // That might require making name-resolution incremental, though. if !sess.opts.unstable_opts.borrowck_unreachable { + // Make sure the user sees dead code warnings, so they know which items aren't checked. + // Normally that's done by `tcx.analysis()`, but we bypass that here. + queries.global_ctxt()?.peek_mut().enter(|tcx| { + tcx.hir() + .par_for_each_module(|module| tcx.ensure().check_mod_deathness(module)); + }); return queries.linker().map(Some); } diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 4530fcab28c50..8924952e3e4f5 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -216,7 +216,7 @@ impl<'tcx> Queries<'tcx> { let crate_name = self.crate_name()?.peek().clone(); let outputs = self.prepare_outputs()?.peek().clone(); let dep_graph = self.dep_graph()?.peek().clone(); - let (krate, resolver, lint_store) = self.expansion()?.take(); + let (krate, resolver, lint_store) = self.expansion()?.peek().clone(); Ok(passes::create_global_ctxt( self.compiler, lint_store, diff --git a/src/test/ui/fuckit/no-borrowck-dead-code.rs b/src/test/ui/fuckit/no-borrowck-dead-code.rs index 3f28416691e7f..0c2934aaaedf6 100644 --- a/src/test/ui/fuckit/no-borrowck-dead-code.rs +++ b/src/test/ui/fuckit/no-borrowck-dead-code.rs @@ -1,7 +1,7 @@ // compile-flags: -Z borrowck-unreachable=no // run-pass -fn dead_code(s: &str) -> &'static str { +fn dead_code(s: &str) -> &'static str { //~ WARNING dead_code s } diff --git a/src/test/ui/fuckit/no-borrowck-dead-code.stderr b/src/test/ui/fuckit/no-borrowck-dead-code.stderr new file mode 100644 index 0000000000000..b08af3866b57b --- /dev/null +++ b/src/test/ui/fuckit/no-borrowck-dead-code.stderr @@ -0,0 +1,10 @@ +warning: function `dead_code` is never used + --> $DIR/no-borrowck-dead-code.rs:4:4 + | +LL | fn dead_code(s: &str) -> &'static str { + | ^^^^^^^^^ + | + = note: `#[warn(dead_code)]` on by default + +warning: 1 warning emitted + diff --git a/src/test/ui/fuckit/no-typeck-dead-code.rs b/src/test/ui/fuckit/no-typeck-dead-code.rs new file mode 100644 index 0000000000000..038058152b3f1 --- /dev/null +++ b/src/test/ui/fuckit/no-typeck-dead-code.rs @@ -0,0 +1,10 @@ +// compile-flags: -Z borrowck-unreachable=no +// run-pass + +fn dead_code(s: &str) -> bool { //~ WARNING never used + true +} + +fn main() { + println!("he he he") +} diff --git a/src/test/ui/fuckit/no-typeck-dead-code.stderr b/src/test/ui/fuckit/no-typeck-dead-code.stderr new file mode 100644 index 0000000000000..0cac8d85b2f87 --- /dev/null +++ b/src/test/ui/fuckit/no-typeck-dead-code.stderr @@ -0,0 +1,10 @@ +warning: function `dead_code` is never used + --> $DIR/no-typeck-dead-code.rs:4:4 + | +LL | fn dead_code(s: &str) -> bool { + | ^^^^^^^^^ + | + = note: `#[warn(dead_code)]` on by default + +warning: 1 warning emitted + diff --git a/src/test/ui/fuckit/typeck-live-code.rs b/src/test/ui/fuckit/typeck-live-code.rs new file mode 100644 index 0000000000000..bab3ee257e381 --- /dev/null +++ b/src/test/ui/fuckit/typeck-live-code.rs @@ -0,0 +1,8 @@ +// compile-flags: -Z borrowck-unreachable=no +fn live_code(s: &str) -> bool { + s //~ ERROR mismatched types +} + +fn main() { + println!("{}", live_code("he he he")); +} diff --git a/src/test/ui/fuckit/typeck-live-code.stderr b/src/test/ui/fuckit/typeck-live-code.stderr new file mode 100644 index 0000000000000..b7ba37dff8339 --- /dev/null +++ b/src/test/ui/fuckit/typeck-live-code.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> $DIR/typeck-live-code.rs:3:5 + | +LL | fn live_code(s: &str) -> bool { + | ---- expected `bool` because of return type +LL | s + | ^ expected `bool`, found `&str` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`.