Assuming my current proposal about using “can import” for build configuration tests is accepted, I’d like to tackle the outstanding question of
#if debug next. This is slightly problematic because as I learned, “debug” can mean many things to different people.
For example, David Owens II writes,
I have a set of test automation that I want to ensure works. However, running this automation with a blocking assert is not what I want. Instead, I’ve created a mechanism for asserts, configurable at runtime, for the way in which those asserts are handled. During automation, I want those asserts to simply log, but code execution is not halted. During my normal dev cycle though, I want those asserts to be raised immediately so the developer sees the error and can chose to investigate or ignore the assert.
I suspect that despite this, that there is a value to supporting a general
#if debug configuration test that doesn’t require you to explicitly set command line flags with
-D <#flag#> and then test for the named flag. I’m hoping that a general utility that’s true for unoptimized builds where asserts can fire is useful enough for a sufficiently large group When I first brought up this topic, I put it this way:
Figuring out what debug *means* is an important first step. To “my people”, it’s the Xcode Build Configuration > Debug scheme setting. For language purposes, the only definition I can come up with at the moment is that debug is what happens when asserts can fire and are not disabled by compile-time optimizations.
Is that a sufficiently convenient definition for a wide enough variety of use cases that it’s worth proposing and putting the effort in to make sure this is included into the language? I need a community consensus before I commit the evolution list to another protracted discussion let alone a formal review. Your feedback will certainly help.
I think there are good reasons to support a debug flag to alter the function of a program while under development. i.e. In order to present different or more detailed information or to log or capture information differently. However, more and more I feel like “debug only” assertions are a code smell. When used improperly, assertions that behave differently for “Release” can create situations where invalid data gets persisted, or crashes become hard to reproduce.
I have been bitten more than once by code that behaved differently in debug builds to release builds. I really distrust anything that makes debug code different to release code. I tolerate code optimisation and asserts compiling to null statements in release code because of the performance gains, but nothing else.
The worst occasion was in diagnosing an intermittent crash in a scanning app written in MS Visual C++. The problem only occurred in the release build and because of the optimisation and symbol stripping, it was virtually impossible to run the release build in a debugger. Furthermore, there were no `#ifdef DEBUG` lines in the source code, so as far as we could tell, the source code was identical in release and build.
Eventually, we discovered (with copious log messages) that a certain initialisation code path (a long way from the crash site) was different in release and debug and the reason it was different was because was an uninitialised boolean variable, that always evaluated to true in debug but frequently did not in release mode. It turned out that, in debug mode, all new stack frames and heap allocated memory were filled with a non zero blank pattern. In release mode, you got whatever garbage was there before.
So, no #if debug please. I want debug builds to behave as far as possible exactly like release builds.
To solve the issue of wanting assert to behave differently for automated tests, I think a different solution is warranted. Maybe add the capability to override normal crashing behaviour by injecting a closure.