August 01, 2014
On Thursday, 31 July 2014 at 20:49:18 UTC, Timon Gehr wrote:
> On 07/31/2014 09:37 PM, Jonathan M Davis wrote:
>> disable contracts, turn assert(0) into a halt
>> instruction, and disable bounds checking in @system and @trusted code.
>> So, if you want to keep the assertions and contracts and whatnot in,
>
> Unfortunately, if used pervasively, assertions and contracts and whatnot may actually hog the speed of a program in a way that breaks the deal.
>
> Disabling assertions (and whatnot), assuming assertions to be true (and disabling whatnot) and leaving all assertions and whatnot in are different trade-offs, of which assuming all assertions to be true is the most dangerous one. Why hide this behaviour in '-release'?

I'm afraid that I don't see the problem. If you want assertions left in in your release/production builds, then don't use -release. If you want them removed, then use -release. Are you objecting to the fact that the compiler can do further optimizations based on the fact that the assertions are presumed to be true if they're removed? I really don't see any problem with that. You're screwed regardless if the assertions would have failed. By their very nature, if an assertion would have failed, your program is an invalid state, so if you don't want to risk having code run that would have failed an assertion, then just don't compile with -release. And if you are willing to assume that the assertions won't fail and have them disabled in your release build, then you might as well gain any extra optimizations that can be made from assuming that the assertion is true. You're already making that assumption anyway and potentially letting your program enter an invalid state.

>> just don't use -release and use -boundscheck=safe to get the bounds
>> checking changes that -release does.
>>
>> - Jonathan M Davis
>
> This leaves assertions and contracts in though.

Which is precisely what Ary was looking to do. He wanted to use -release but not have assertions removed, and using -boundscheck=safe instead of -release does that (though further posts by him seem to indicate that he doesn't want the @system bounds checking removed either, which means that he should just skip -release and all related flags entirely).

- Jonathan M Davis
August 01, 2014
On Thursday, 31 July 2014 at 20:49:18 UTC, Timon Gehr wrote:
> On 07/31/2014 09:37 PM, Jonathan M Davis wrote:
>> On Thursday, 31 July 2014 at 18:43:49 UTC, Ary Borenszweig wrote:
>>> On 7/31/14, 4:37 AM, Walter Bright wrote:
>>>> On 7/30/2014 4:05 PM, Ary Borenszweig wrote:
>>>>> On 7/30/14, 7:01 PM, Walter Bright wrote:

> Disabling assertions (and whatnot), assuming assertions to be true (and disabling whatnot) and leaving all assertions and whatnot in are different trade-offs, of which assuming all assertions to be true is the most dangerous one. Why hide this behaviour in '-release'?

But assertions are *always* assumed to be true. The sole difference is that in Debug mode actual code generation for their check is disabled (not exactly the assertions, but code testing them).

The compiler makes no guarantee on the code that comes after an assert other than it will work correctly *if the assertion holds*. This is true for the code generated in both Debug and Release builds.

The difference is in the level of optimization (to play nice with gdb) and checks: in the Debug mode, that "assert-compelling generated code" never gets to actually execute as the program chocks at the assertion point (that is, just before entering the code).
August 01, 2014
On Friday, 1 August 2014 at 05:27:48 UTC, eles wrote:
> On Thursday, 31 July 2014 at 20:49:18 UTC, Timon Gehr wrote:
>> On 07/31/2014 09:37 PM, Jonathan M Davis wrote:
>>> On Thursday, 31 July 2014 at 18:43:49 UTC, Ary Borenszweig wrote:
>>>> On 7/31/14, 4:37 AM, Walter Bright wrote:
>>>>> On 7/30/2014 4:05 PM, Ary Borenszweig wrote:
>>>>>> On 7/30/14, 7:01 PM, Walter Bright wrote:
>
>> Disabling assertions (and whatnot), assuming assertions to be true (and disabling whatnot) and leaving all assertions and whatnot in are different trade-offs, of which assuming all assertions to be true is the most dangerous one. Why hide this behaviour in '-release'?
>
> But assertions are *always* assumed to be true. The sole difference is that in Debug mode actual code generation for

*Release mode
August 01, 2014
On Thursday, 31 July 2014 at 21:29:59 UTC, Sean Kelly wrote:
> On Thursday, 31 July 2014 at 21:11:17 UTC, Walter Bright wrote:
>> On 7/31/2014 1:52 PM, Sean Kelly wrote:
>>> Could you expand on what you consider input?
>>
>> All state processed by the program that comes from outside the program. That would include:
>>
>> 1. user input
>> 2. the file system
>> 3. uninitialized memory
>> 4. interprocess shared memory
>> 5. anything received from system APIs, device drivers, and DLLs that are not part of the program
>> 6. resource availability and exhaustion
>
> So effectively, any factor occurring at runtime.  If I create a
> library, it is acceptable to validate function parameters using
> assert() because the user of that library knows what the library
> expects and should write their code accordingly.  That's fair.

Yes. It basically comes down to whether you would consider incorrect input to the function to be an outright program error that should _never_ occur (in which case, you use assertions) or whether you consider it reasonable for bad input to be given some of the time or unreasonable to require that the caller never give bad input (in which case, you use exceptions).

Also, if efficiency is of great concern and it's possible for the caller to guarantee that the input is valid (which wouldn't be the case with something like files, because even if the input was validated, it could become invalid afterwords), then using assertions might be a better approach. On the other hand, if correctness is of greater concern, then it might make more sense to use exceptions, because then it makes sure that the programmer is never able to give bad input (though if it really makes more sense to treat that as program bug - e.g. giving an invalid index - then using Errors or assert(0) might make more sense, since they'd stay in but be immediately fatal).

All in all, while some cases are very clear-cut as to whether assertions or exceptions should be used, many of other cases are highly subjective, at which point it generally comes down to whether bad input to the function should be considered a programming error or whether it makes more sense for the function to throw and allow the program to try and recover from the problem.

- Jonathan M Davis
August 01, 2014
On Thursday, 31 July 2014 at 19:31:51 UTC, Jonathan M Davis wrote:
> The whole type is templated, so the assertions will be compiled in based on whether the user's code is compiled with -released or not.

Sounds tricky. Doesn't the compiler optimize template instantiation? If it finds an already compiled template instance somewhere, it will use it instead of generating new one.
August 01, 2014
On Friday, 1 August 2014 at 06:53:17 UTC, Kagamin wrote:
> On Thursday, 31 July 2014 at 19:31:51 UTC, Jonathan M Davis wrote:
>> The whole type is templated, so the assertions will be compiled in based on whether the user's code is compiled with -released or not.
>
> Sounds tricky. Doesn't the compiler optimize template instantiation? If it finds an already compiled template instance somewhere, it will use it instead of generating new one.

Since all template instantiations must happen when you compile your program rather than in any libraries you're linking against, why would it matter? If you compile your program without -release, then all assertions in all templates that your program uses will be enabled. The only cases where it wouldn't be would be when a library that you're linking against uses that template internally, but then it's that library's code and not yours, so it's probably out of your hands to deal with assertion failures in it anyway. Any template reuse that occurred would be reuse within your program and thus use the same flags. The only way that I can think of that that could be screwed up is if you're compiling each module separately and use different flags for each, but then each module which was compiled with -release wouldn't have assertions and those that weren't would. The compiler would never try to share any template instantiations across them. They wouldn't even be involved with each other until the linker was run on them, so the compiler wouldn't even have the chance to try and share anything between them.

- Jonathan M Davis
August 01, 2014
On Friday, 1 August 2014 at 01:20:04 UTC, Walter Bright wrote:
> On 7/31/2014 3:07 PM, David Bregman wrote:
>> On Thursday, 31 July 2014 at 18:58:11 UTC, Walter Bright wrote:
>>> On 7/31/2014 4:28 AM, David Bregman wrote:
>>>> Sigh. Of course you can assume the condition after a runtime check has been
>>>> inserted. You just showed that
>>>>
>>>> assert(x); assume(x);
>>>>
>>>> is semantically equivalent to
>>>> assert(x);
>>>>
>>>> as long as the runtime check is not elided. (no -release)
>>>
>>> No. I showed that you cannot have an assert without the assume. That makes
>>> them equivalent that direction.
>>
>> That is only true if assert always generates a runtime check. i.e. it is not
>> true for C/C++ assert (and so far, D assert) in release mode.
>>
>>> For the other direction, adding in a runtime check for an assume is going to
>>> be expected of an implementation.
>>
>> No. It is expected that assume does /not/ have a runtime check. Assume is used
>> to help the compiler optimize based on trusted facts, doing a runtime check
>> could easily defeat the purpose of such micro optimizations.
>
> I'm rather astonished you'd take that position. It opens a huge door wide for undefined behavior, and no obvious way of verifying that the assume() is correct.

It's not a "position". I'm just giving you the definition of assume so you can understand the difference from assert.

> I'm confident that if D introduced such behavior, the very first comment would be "I need it to insert a runtime check on demand."
>
>
>>> And, in fact, since the runtime check won't change the semantics if the assume
>>> is correct, they are equivalent.
>>
>> Right, only "if the assume is correct". So they aren't equivalent if it isn't
>> correct.
>>
>> Q.E.D. ?
>
> I'm not buying those uncheckable semantics as being workable and practical.

I don't know what this means. Are you saying that you refuse to admit the difference between assert and assume because you exclude the possibility of the expression being false (buggy)?

>
>>>> But you still want to assert to become assume in release mode? How
>>>> will you handle the safety issue?
>>>
>>> I don't know yet.
>>
>> I would think the easiest way is to just not inject the assumption when inside
>> @safe code, but I don't know anything about the compiler internals.
>>
>> Even for @system code, I'm on the fence about whether asserts should affect
>> codegen in release, it doesn't seem like a clear tradeoff to make: safety vs
>> some dubious optimization gains.
>
> So why do you want assume() with no checking whatsoever? Does anybody want that? Why are we even discussing such a misfeature?

Ever since my first reply, I've only been trying to
a) help you to understand the difference between the two, and
b) to highlight the safety issue with assume (and the proposal for assert to become assume in release mode).

Whether I want assume or not is beside the point. If you've interpreted my posts as some kind of lobbying for the addition of assume() to D, that is not the case. If you want my opinion on that, I think it would be a good thing to have assume and assert separate, for safety and clarity reasons. However the importance I would assign to adding assume() is low because it is only useful for very specific micro optimizations, and is always a tradeoff with its unsafety. In C, I typically use it only a couple times in a large code base, compared to assert which I use all over the place.

>
>> Do we really want to go down the same road as C
>> with undefined behavior?
>
> So you don't want assume()? Who does?

I do want assume to some extent (see above), but it wasn't my point.
August 01, 2014
On Thursday, 31 July 2014 at 21:29:59 UTC, Sean Kelly wrote:
> So effectively, any factor occurring at runtime.  If I create a
> library, it is acceptable to validate function parameters using
> assert() because the user of that library knows what the library
> expects and should write their code accordingly.  That's fair.

He should, but what if he doesn't and the library is phobos or druntime (which are compiled in release mode)? BTW, druntime can't use enforce and doesn't verify its input.
August 01, 2014
On Friday, 1 August 2014 at 04:12:40 UTC, Walter Bright wrote:
> On 7/31/2014 2:21 PM, Sean Kelly wrote:
>> Thoughts?
>
> If a process detects a logic error, then that process is in an invalid state that is unanticipated and unknown to the developer. The only correct solution is to halt that process, and all processes that share memory with it.
>
> Anything less is based on faith and hope. If it is medical, flight control, or banking software, I submit that operating on faith and hope is not acceptable.

The release version of software is compiled in release mode, asserts are removed and everything will continue to operate just fine, thankfully.
August 01, 2014
On Thursday, 31 July 2014 at 22:21:46 UTC, Daniel Gibson wrote:
> Am 31.07.2014 23:59, schrieb Walter Bright:
>> On 7/31/2014 10:40 AM, Daniel Gibson wrote:
>>> It's a major PITA to debug problems that only happen in release builds.
>>
>> Debugging optimized code was a well known problem even back in the 70's.
>> Nobody has solved it, and nobody wants unoptimized code.
>>
>
> Yeah, and because of this I'd like optimizations not to cause different behavior if at all possible to keep these kind of bugs as low as possible.
>
> And I agree with your stance on those fine-grained optimization switches from your other post. GCC currently has 191 flags the influence optimization[1] (+ a version that negates them for most), and I don't understand what most of them do, so it would be hard for me to decide which optimizations I want and which I don't want.
>
> However, what about an extra flag for "unsafe" optimizations?
> I'd like the compiler to do inlining, replacing int multiplications with powers of two with shifts and other "safe" optimizations that don't change the semantics of my program (see the examples in the post you quoted), but I *don't* want it to e.g. remove writes to memory that isn't read afterwards or make assumptions based on assertions (that are disabled in the current compile mode).
>
> And maybe a warning mode that tells me about "dead"/"superfluous" code that would be eliminated in an optimized build so I can check if that would break anything for me in that respect without trying to understand the asm output would be helpful.
>

A compiler is a program that turns code in one programming language to equivalent machine code, according to a language specification. There are obviously many different equivalent machine code programs corresponding to any sufficiently complex higher-level program. Classifying them into optimized and unoptimized ones is rather arbitrary. The same goes for safe vs. unsafe optimizations.

To achieve what you want, the compiler would have to ignore the actual language specification and use a different one that is tweaked according to your criteria. I don't think this is desirable. If the official language specification has parts that can lead to the errors you want to avoid, then it's not the compiler's fault, and therefore the compiler should not be changed to workaround it. Instead, deficiencies in the specification should be fixed there.