1 day ago
On Friday, 4 July 2025 at 07:16:17 UTC, Adam Wilson wrote:
> On Friday, 4 July 2025 at 06:29:26 UTC, Walter Bright wrote:
>> On 7/3/2025 12:21 AM, Adam Wilson wrote:
>>> It is an absolute non-negotiable business requirement that I be able to get debugging information out of the server without physical access to the device. If you won't deliver the logging data, corrupted or not, on an assert, then no business can justify using D in production.
>>
>> I did mention that logging the error before terminating the process was acceptable. My point is that recovering is not acceptable.
>
> Kinda hard to do that when the process terminates, especially if the logger is a side-thread of the app like it was on my team at MSFT.
>
> But also, not printing a stack trace means there is nothing to log.

Actually no, as long as the termination is via abort(), then on unix type systems the reliable way to get a trace is via an external monitor program.

I convinced a colleague of this at a prior large company, and offered guidance as he created such a monitor and dump program.  It was sort of like a specialised version of a debugger, making use of the various debugger system facilities.

It was to replace an in process post crash recovery for crash dump mechanism.

The actual process being monitored was able to check in with the monitor at start up, and provide hints as to where interesting pieces of data were based in memory; but once done everything was based upon the monitor extracting information, including back-tracing the stack(s) from outside the crashed process.
1 day ago

On Sunday, 29 June 2025 at 18:04:51 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

Hello!

[...]

Destroy!

Somewhat unrelated to this discussion, but I have come of the opinion that a large portion of asserts are actually people 'protecting' their libraries, which, if they designed them right, wouldn't need an assert in the first place.

You can typically tell where they are when you see documentation like "it is forbidden to call this method before X or after Y".

Often these things can be addressed by encoding the constraints in the types instead, and in the process eliminating any need for an assert at all.

It would be interesting to see how many actual uses of assert in common D libraries are actually the consequence of such design decisions.

1 day ago
On 05/07/2025 12:41 AM, Sebastiaan Koppe wrote:
> On Sunday, 29 June 2025 at 18:04:51 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> Hello!
>>
>> [...]
>>
>> Destroy!
> 
> Somewhat unrelated to this discussion, but I have come of the opinion that a large portion of asserts are actually people 'protecting' their libraries, which, if they designed them right, wouldn't need an assert in the first place.
> 
> You can typically tell where they are when you see documentation like "it is forbidden to call this method before X or after Y".
> 
> Often these things can be addressed by encoding the constraints in the types instead, and in the process eliminating any need for an assert at all.
> 
> It would be interesting to see how many actual uses of assert in common D libraries are actually the consequence of such design decisions.

This is a key reason why I think asserts have to be recoverable.

Unfortunately a very large percentage of usage of it, use it for logic level errors, and these must be recoverable.

Contracts are a good example of this, they are inherently recoverable because they are in a functions API, they are not internal unrecoverable situations!

Its going to be easier to take the smallest use case that is unrecoverable dead process, and use a different mechanism to kill the process in these cases.

1 day ago
On Friday, 4 July 2025 at 07:21:12 UTC, Jonathan M Davis wrote:
> [...] And of course, for each such piece of clean up code that's skipped, the more invalid the state of the program becomes, making it that much riskier for any of the code that does run while the stack unwinds to log any information about the state of the program.

Maybe it's a heretical question: What kind of software do you write?
Why does the program state matter at all? I think that the process
state may be corrupted as long as the "physical data model"
remains unimpaired, e.g. does not get updated by the corrupted
process. If the physical data model does not live in the process of
course.


1 day ago
On Friday, July 4, 2025 6:48:21 AM Mountain Daylight Time Richard (Rikki) Andrew Cattermole via Digitalmars-d wrote:
> Contracts are a good example of this, they are inherently recoverable because they are in a functions API, they are not internal unrecoverable situations!

Not really, no. The point of contracts is to find bugs in the calling code, because the function has requirements about how it must be called, and it's considered a bug if the function is called with arguments that do not meet those requirements. The assertions are then compiled out in release mode, because they're simply there to find bugs, not to be part of the function's API.

On the other hand, if a function is designed to treat the arguments as user input or is otherwise designed to defend itself against bad input rather than treating bad arguments as a bug, then it should be using Exceptions and not contracts. The fact that they're thrown is then essentially part of the function's API, and they need to be left in in release builds.

By definition, assertions are only supposed to be used to catch bugs in a program, not for defending against bad input. They're specifically there to catch bugs rather than protect against bad input, whereas Exceptions are left in permanently, because they're there to protect against bad user input or problems in the environment which are not caused by bugs in the program.

- Jonathan M Davis




1 day ago
On 05/07/2025 4:18 AM, Jonathan M Davis wrote:
> On Friday, July 4, 2025 6:48:21 AM Mountain Daylight Time Richard (Rikki) Andrew Cattermole via Digitalmars-d wrote:
>> Contracts are a good example of this, they are inherently recoverable
>> because they are in a functions API, they are not internal unrecoverable
>> situations!
> 
> Not really, no. The point of contracts is to find bugs in the calling code,
> because the function has requirements about how it must be called, and it's
> considered a bug if the function is called with arguments that do not meet
> those requirements. The assertions are then compiled out in release mode,
> because they're simply there to find bugs, not to be part of the function's
> API.
> 
> On the other hand, if a function is designed to treat the arguments as user
> input or is otherwise designed to defend itself against bad input rather
> than treating bad arguments as a bug, then it should be using Exceptions and
> not contracts. The fact that they're thrown is then essentially part of the
> function's API, and they need to be left in in release builds.
> 
> By definition, assertions are only supposed to be used to catch bugs in a
> program, not for defending against bad input. They're specifically there to
> catch bugs rather than protect against bad input, whereas Exceptions are
> left in permanently, because they're there to protect against bad user input
> or problems in the environment which are not caused by bugs in the program.
> 
> - Jonathan M Davis

This is exactly my point. They are tuned wrong.

They are currently acting as an internal detail of a function, and that has no business being exposed at the function API level.

Other programmers do not need this information.

What they need is logic level criteria for a function to work.

The purpose of contracts first and foremost is to document the requirements of a called function and they are currently not succeeding at this job, because their focus is on internal details rather than external.
1 day ago
On Friday, July 4, 2025 10:30:02 AM Mountain Daylight Time Richard (Rikki) Andrew Cattermole via Digitalmars-d wrote:
> On 05/07/2025 4:18 AM, Jonathan M Davis wrote:
> This is exactly my point. They are tuned wrong.
>
> They are currently acting as an internal detail of a function, and that has no business being exposed at the function API level.
>
> Other programmers do not need this information.
>
> What they need is logic level criteria for a function to work.
>
> The purpose of contracts first and foremost is to document the requirements of a called function and they are currently not succeeding at this job, because their focus is on internal details rather than external.

I don't know why you think that their focus is on internal details. At least with in contracts, they're used to assert the state of the function's arguments. They're verifying that the caller is following the contract that the function gives for its input, and if the documentation is written correctly, then those requirements are in the documentation. And if the caller passes any arguments which fail the contract, then it's a bug in the caller. So, the contracts are essentially test code for the calling code.

Now, contracts as they currently stand in D _are_ flawed, but not because of how assertions work. They're flawed because of how the contracts themselves are implemented.

How they should have been implemented is for them to be compiled in based on the compilation flags of the caller, not the ones used when compiling the function itself. So, if you were using a library with contracts, and you compiled your code with assertions compiled in, then you'd get the checks that are in the contracts, and if you compiled without assertions (presumably, because it was a production build), then they wouldn't be compiled in. They're testing the caller's code, not the function's code, so whether the contracts are compiled in should depend on how the caller is compiled.

However, the way that contracts are currently implemented is that they're part of the function itself instead of being attached to it, and whether they're compiled in or not depends on how the function itself is compiled. This means that contracts are effectively broken unless they're used in templated code.

And that's why personally, I never use contracts. I think that the idea is sound, but the implementation is flawed due to how they're compiled in.

So, ignoring the issue of classes, I don't at all agree that AssertErrors need to be recoverable, because they're used in contracts. Contracts are like any other assertion in the sense that they're catching a bug in the code, not validating user input.

That being said, the fact that the contracts on virtual functions have to catch AssertErrors and potentially ignore them (due to the relaxing or tightening of contracts based on inheritance) means that yes, AssertErrors need to be recoverable in at least the context of classes. The programmer really shouldn't be trying to recover from AssertErrors, but the runtime actually has to within that limited context - and for that to work properly, destructors and other clean up code actually needs to work properly when AssertErrors are thrown in the contracts of virtual functions.

But if you're arguing that programmers should be trying to recover from failed contracts, then I don't agree at all. They're intended for catching code that fails to stick to a function's contract in debug builds, and that's really not a situation where there should even be a need to consider recovering from an AssertError.

If a programmer wants to write a function where the arguments are checked, and the intention is that the program will recover when bad arguments are given, then Exceptions should be used, not assertions.

- Jonathan M Davis




1 day ago
On 7/4/2025 12:21 AM, Jonathan M Davis wrote:
> Even if recovering is not acceptable, if the proper clean up is done when
> the stack is unwound, then it's possible to use destructors, scope
> statements, and catch blocks to get additional information about the state
> of the program as the stack unwinds. If the proper clean up is not done as
> the stack unwinds, then those destructors, scope statements, and catch
> statements will either not be run (meaning that any debugging information
> which could have been obtained from them wouldn't be), and/or only some of
> them will be run. And of course, for each such piece of clean up code that's
> skipped, the more invalid the state of the program becomes, making it that
> much riskier for any of the code that does run while the stack unwinds to
> log any information about the state of the program.

Executing clean up code (i.e. destructors) is not at all about logging errors, because they happen through the normal error-free operation of the program. If you were logging normal usage, you'd want the logs from before the fault happened, not after.

23 hours ago
On 7/4/25 12:11, Dennis wrote:
> On Friday, 4 July 2025 at 09:56:53 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> scope(exit) is ran when Error passes through it.
>>
>> This is one of the complicating factors at play.
> 
> scope guards and destructor calls are internally lowered to finally blocks, they're all treated the same. But let's say they aren't, that still doesn't answer the question: what error logging code are you writing that relies on clean up code being run? What does the output look like with and without?

With: It writes a file with the full interaction log that leads to the crash. The user can see the stack trace in a console window that is kept open using `system("pause")`. They can send the data to me and I can immediately reproduce the issue and fix the crash within 24 hours.

Without: The program randomly closes on the user's machine and I get no further information. I can only speculate what is the cause. It might be bad design of the D language, bad defaults, a bug in a (e.g., C) dependency, etc. I have no idea. I get one of these reports at most once every couple of months, so this is not at the top of my list of priorities, even if I know there are further things to try that may or may not lead to more information being available.

Anything that causes the second scenario I will strongly oppose, even if it's just a default setting.
22 hours ago
On 7/4/2025 11:49 AM, Timon Gehr wrote:
> With: It writes a file with the full interaction log that leads to the crash.

Cleanup code is what is happening following the crash. If you're logging, it would be the entry code to the function, not the cleanup. What you can do is collect and log a stack trace, but that isn't cleanup code.

> The user can see the stack trace in a console window that is kept open using `system("pause")`. They can send the data to me and I can immediately reproduce the issue and fix the crash within 24 hours.

I'm not objecting to a stack trace.