From d5d834551cd5a7e7d89ac9f2ae642a93409ed989 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 2 Mar 2015 14:51:24 -0800 Subject: [PATCH] rustc: Add a debug_assertions #[cfg] directive This commit is an implementation of [RFC 563][rfc] which adds a new `cfg(debug_assertions)` directive which is specially recognized and calculated by the compiler. The flag is turned off at any optimization level greater than 1 and may also be explicitly controlled through the `-C debug-assertions` flag. [rfc]: https://github.com/rust-lang/rfcs/pull/563 The `debug_assert!` and `debug_assert_eq!` macros now respect this instead of the `ndebug` variable and `ndebug` no longer holds any meaning to the standard library. Code which was previously relying on `not(ndebug)` to gate expensive code should be updated to rely on `debug_assertions` instead. Closes #22492 [breaking-change] --- mk/tests.mk | 2 +- src/libcore/macros.rs | 24 ++++++----- src/liblog/macros.rs | 4 +- src/librustc/session/config.rs | 16 +++++-- src/librustc_trans/trans/base.rs | 2 +- src/test/run-fail/overflowing-add.rs | 1 + src/test/run-fail/overflowing-mul.rs | 1 + src/test/run-fail/overflowing-sub.rs | 1 + src/test/run-make/debug-assertions/Makefile | 21 ++++++++++ src/test/run-make/debug-assertions/debug.rs | 42 +++++++++++++++++++ .../run-pass/conditional-debug-macro-off.rs | 2 +- src/test/run-pass/logging-enabled-debug.rs | 2 +- src/test/run-pass/logging-separate-lines.rs | 1 + 13 files changed, 100 insertions(+), 19 deletions(-) create mode 100644 src/test/run-make/debug-assertions/Makefile create mode 100644 src/test/run-make/debug-assertions/debug.rs diff --git a/mk/tests.mk b/mk/tests.mk index 838ed0dccfc..ef38abcab65 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -590,7 +590,7 @@ TEST_SREQ$(1)_T_$(2)_H_$(3) = \ # The tests select when to use debug configuration on their own; # remove directive, if present, from CFG_RUSTC_FLAGS (issue #7898). -CTEST_RUSTC_FLAGS := $$(subst --cfg ndebug,,$$(CFG_RUSTC_FLAGS)) +CTEST_RUSTC_FLAGS := $$(subst -C debug-assertions,,$$(CFG_RUSTC_FLAGS)) # The tests cannot be optimized while the rest of the compiler is optimized, so # filter out the optimization (if any) from rustc and then figure out if we need diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index 94ca9ec37b4..96c9beece1c 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -100,10 +100,12 @@ macro_rules! assert_eq { /// This will invoke the `panic!` macro if the provided expression cannot be /// evaluated to `true` at runtime. /// -/// Unlike `assert!`, `debug_assert!` statements can be disabled by passing -/// `--cfg ndebug` to the compiler. This makes `debug_assert!` useful for -/// checks that are too expensive to be present in a release build but may be -/// helpful during development. +/// Unlike `assert!`, `debug_assert!` statements are only enabled in non +/// optimized builds by default. An optimized build will omit all +/// `debug_assert!` statements unless `-C debug-assertions` is passed to the +/// compiler. This makes `debug_assert!` useful for checks that are too +/// expensive to be present in a release build but may be helpful during +/// development. /// /// # Example /// @@ -125,7 +127,7 @@ macro_rules! assert_eq { #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] macro_rules! debug_assert { - ($($arg:tt)*) => (if cfg!(not(ndebug)) { assert!($($arg)*); }) + ($($arg:tt)*) => (if cfg!(debug_assertions) { assert!($($arg)*); }) } /// Asserts that two expressions are equal to each other, testing equality in @@ -133,10 +135,12 @@ macro_rules! debug_assert { /// /// On panic, this macro will print the values of the expressions. /// -/// Unlike `assert_eq!`, `debug_assert_eq!` statements can be disabled by -/// passing `--cfg ndebug` to the compiler. This makes `debug_assert_eq!` -/// useful for checks that are too expensive to be present in a release build -/// but may be helpful during development. +/// Unlike `assert_eq!`, `debug_assert_eq!` statements are only enabled in non +/// optimized builds by default. An optimized build will omit all +/// `debug_assert_eq!` statements unless `-C debug-assertions` is passed to the +/// compiler. This makes `debug_assert_eq!` useful for checks that are too +/// expensive to be present in a release build but may be helpful during +/// development. /// /// # Example /// @@ -147,7 +151,7 @@ macro_rules! debug_assert { /// ``` #[macro_export] macro_rules! debug_assert_eq { - ($($arg:tt)*) => (if cfg!(not(ndebug)) { assert_eq!($($arg)*); }) + ($($arg:tt)*) => (if cfg!(debug_assertions) { assert_eq!($($arg)*); }) } /// Short circuiting evaluation on Err diff --git a/src/liblog/macros.rs b/src/liblog/macros.rs index 4a9a9bd4060..f0f861a3831 100644 --- a/src/liblog/macros.rs +++ b/src/liblog/macros.rs @@ -157,7 +157,7 @@ macro_rules! info { /// ``` #[macro_export] macro_rules! debug { - ($($arg:tt)*) => (if cfg!(not(ndebug)) { log!(::log::DEBUG, $($arg)*) }) + ($($arg:tt)*) => (if cfg!(debug_assertions) { log!(::log::DEBUG, $($arg)*) }) } /// A macro to test whether a log level is enabled for the current module. @@ -192,7 +192,7 @@ macro_rules! debug { macro_rules! log_enabled { ($lvl:expr) => ({ let lvl = $lvl; - (lvl != ::log::DEBUG || cfg!(not(ndebug))) && + (lvl != ::log::DEBUG || cfg!(debug_assertions)) && lvl <= ::log::log_level() && ::log::mod_enabled(lvl, module_path!()) }) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index a4355803f4a..1b09be05020 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -81,6 +81,7 @@ pub struct Options { pub gc: bool, pub optimize: OptLevel, + pub debug_assertions: bool, pub debuginfo: DebugInfoLevel, pub lint_opts: Vec<(String, lint::Level)>, pub describe_lints: bool, @@ -238,7 +239,8 @@ pub fn basic_options() -> Options { crate_name: None, alt_std_name: None, libs: Vec::new(), - unstable_features: UnstableFeatures::Disallow + unstable_features: UnstableFeatures::Disallow, + debug_assertions: true, } } @@ -528,6 +530,8 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, 2 = full debug info with variable and type information"), opt_level: Option = (None, parse_opt_uint, "Optimize with possible levels 0-3"), + debug_assertions: Option = (None, parse_opt_bool, + "explicitly enable the cfg(debug_assertions) directive"), } @@ -621,7 +625,7 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig { }; let mk = attr::mk_name_value_item_str; - return vec!(// Target bindings. + let mut ret = vec![ // Target bindings. attr::mk_word_item(fam.clone()), mk(InternedString::new("target_os"), intern(os)), mk(InternedString::new("target_family"), fam), @@ -629,7 +633,11 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig { mk(InternedString::new("target_endian"), intern(end)), mk(InternedString::new("target_pointer_width"), intern(wordsz)) - ); + ]; + if sess.opts.debug_assertions { + ret.push(attr::mk_word_item(InternedString::new("debug_assertions"))); + } + return ret; } pub fn append_configuration(cfg: &mut ast::CrateConfig, @@ -923,6 +931,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { } } }; + let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == No); let gc = debugging_opts.gc; let debuginfo = if matches.opt_present("g") { if cg.debuginfo.is_some() { @@ -1064,6 +1073,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { alt_std_name: None, libs: libs, unstable_features: get_unstable_features_setting(), + debug_assertions: debug_assertions, } } diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 13f882bc363..cf4d0e543e3 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -3089,7 +3089,7 @@ pub fn trans_crate<'tcx>(analysis: ty::CrateAnalysis<'tcx>) let check_overflow = if let Some(v) = tcx.sess.opts.debugging_opts.force_overflow_checks { v } else { - !attr::contains_name(&krate.config, "ndebug") + tcx.sess.opts.debug_assertions }; // Before we touch LLVM, make sure that multithreading is enabled. diff --git a/src/test/run-fail/overflowing-add.rs b/src/test/run-fail/overflowing-add.rs index 34a03e5f008..cd13b817c2b 100644 --- a/src/test/run-fail/overflowing-add.rs +++ b/src/test/run-fail/overflowing-add.rs @@ -9,6 +9,7 @@ // except according to those terms. // error-pattern:thread '
' panicked at 'arithmetic operation overflowed' +// compile-flags: -C debug-assertions // (Work around constant-evaluation) fn value() -> u8 { 200 } diff --git a/src/test/run-fail/overflowing-mul.rs b/src/test/run-fail/overflowing-mul.rs index b18d99cd232..5d2f5396240 100644 --- a/src/test/run-fail/overflowing-mul.rs +++ b/src/test/run-fail/overflowing-mul.rs @@ -9,6 +9,7 @@ // except according to those terms. // error-pattern:thread '
' panicked at 'arithmetic operation overflowed' +// compile-flags: -C debug-assertions // (Work around constant-evaluation) fn value() -> u8 { 200 } diff --git a/src/test/run-fail/overflowing-sub.rs b/src/test/run-fail/overflowing-sub.rs index ee32291eca6..b089dccbaa5 100644 --- a/src/test/run-fail/overflowing-sub.rs +++ b/src/test/run-fail/overflowing-sub.rs @@ -9,6 +9,7 @@ // except according to those terms. // error-pattern:thread '
' panicked at 'arithmetic operation overflowed' +// compile-flags: -C debug-assertions // (Work around constant-evaluation) fn value() -> u8 { 42 } diff --git a/src/test/run-make/debug-assertions/Makefile b/src/test/run-make/debug-assertions/Makefile new file mode 100644 index 00000000000..71297562768 --- /dev/null +++ b/src/test/run-make/debug-assertions/Makefile @@ -0,0 +1,21 @@ +-include ../tools.mk + +all: + $(RUSTC) debug.rs -C debug-assertions=no + $(call RUN,debug) good + $(RUSTC) debug.rs -C opt-level=0 + $(call RUN,debug) bad + $(RUSTC) debug.rs -C opt-level=1 + $(call RUN,debug) good + $(RUSTC) debug.rs -C opt-level=2 + $(call RUN,debug) good + $(RUSTC) debug.rs -C opt-level=3 + $(call RUN,debug) good + $(RUSTC) debug.rs -O + $(call RUN,debug) good + $(RUSTC) debug.rs + $(call RUN,debug) bad + $(RUSTC) debug.rs -C debug-assertions=yes -O + $(call RUN,debug) bad + $(RUSTC) debug.rs -C debug-assertions=yes -C opt-level=1 + $(call RUN,debug) bad diff --git a/src/test/run-make/debug-assertions/debug.rs b/src/test/run-make/debug-assertions/debug.rs new file mode 100644 index 00000000000..a0ccc75afd0 --- /dev/null +++ b/src/test/run-make/debug-assertions/debug.rs @@ -0,0 +1,42 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(warnings)] + +use std::env; +use std::thread; + +fn main() { + let should_fail = env::args().nth(1) == Some("bad".to_string()); + + assert_eq!(thread::spawn(debug_assert_eq).join().is_err(), should_fail); + assert_eq!(thread::spawn(debug_assert).join().is_err(), should_fail); + assert_eq!(thread::spawn(overflow).join().is_err(), should_fail); +} + +fn debug_assert_eq() { + let mut hit1 = false; + let mut hit2 = false; + debug_assert_eq!({ hit1 = true; 1 }, { hit2 = true; 2 }); + assert!(!hit1); + assert!(!hit2); +} + +fn debug_assert() { + let mut hit = false; + debug_assert!({ hit = true; false }); + assert!(!hit); +} + +fn overflow() { + fn add(a: u8, b: u8) -> u8 { a + b } + + add(200u8, 200u8); +} diff --git a/src/test/run-pass/conditional-debug-macro-off.rs b/src/test/run-pass/conditional-debug-macro-off.rs index b5a5f57d07a..90142350772 100644 --- a/src/test/run-pass/conditional-debug-macro-off.rs +++ b/src/test/run-pass/conditional-debug-macro-off.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags: --cfg ndebug +// compile-flags: -C debug-assertions=no // exec-env:RUST_LOG=conditional-debug-macro-off=4 #[macro_use] diff --git a/src/test/run-pass/logging-enabled-debug.rs b/src/test/run-pass/logging-enabled-debug.rs index 262d9b21eb4..dfc92728270 100644 --- a/src/test/run-pass/logging-enabled-debug.rs +++ b/src/test/run-pass/logging-enabled-debug.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags:--cfg ndebug +// compile-flags:-C debug-assertions=no // exec-env:RUST_LOG=logging-enabled-debug=debug #[macro_use] diff --git a/src/test/run-pass/logging-separate-lines.rs b/src/test/run-pass/logging-separate-lines.rs index 8526dfe72da..82a155b1173 100644 --- a/src/test/run-pass/logging-separate-lines.rs +++ b/src/test/run-pass/logging-separate-lines.rs @@ -10,6 +10,7 @@ // ignore-windows // exec-env:RUST_LOG=debug +// compile-flags:-C debug-assertions=y #[macro_use] extern crate log;