Date: Mon, 03 Jul 2023 20:47:15 -0500
On Mon, Jul 3, 2023, at 15:21, Patrice Roy via SG14 wrote:
> Thanks Timur. I took the liberty of adding SG14 to the conversation as I
> think they might have some interest in this specific topic.
>
> Le lun. 3 juil. 2023 à 15:59, Timur Doumler via SG21 <sg21_at_[hidden]>
> a écrit :
>
>> Hello SG21,
>>
>> Here's an issue with the current Contracts design that was brought to my
>> attention by a user from the music technology industry, I am curious what
>> y'all think about it.
>>
>> They are very concerned that failing contract checks, and in particular
>> failing *asserts*, which can appear in the middle of a function body, can
>> call a user-defined violation handler which can be defined to throw an
>> exception. They say that basically this means that anywhere they currently
>> have an assert somewhere deep in their library, the user can turn that
>> assert into a throwing function, without the library knowing about it. So
>> they would have to go off and rewrite all their library code to be
>> exception-safe, which is an unacceptable burden to them.
>>
>> Here's a line of code with an assert in the OpenMPT library:
>> https://github.com/OpenMPT/openmpt/blob/b6a3124344de885f08286ac17880e31247db882b/soundlib/Fastmix.cpp#L184
>>
>>
>> Here's another assert in the same library:
>>
>> https://github.com/OpenMPT/openmpt/blob/b6a3124344de885f08286ac17880e31247db882b/soundlib/Sndfile.cpp#L1874
>>
>>
>> ...which is being called from here:
>>
>> https://github.com/OpenMPT/openmpt/blob/b6a3124344de885f08286ac17880e31247db882b/soundlib/Snd_fx.cpp#L1002
>>
>>
>> This is what the user is saying:
>>
>> "I honestly do not want to invest even a single second thinking about what
>> state any internal data structures are in when these lines suddenly throw.
>> That's not a part of code where we expect any exception, but when migrating
>> the assertions to contracts, we would have to deal with any assertion
>> potentially throwing. I could document that we do not support throwing
>> contract violation handlers (which implies not supporting all situation
>> specified by the C++ standard, and feels wrong), but then a C library that
>> calls into our C++ library (supported, because we provide a facade C
>> interface) would also need to document this, because this C library itself
>> could end up being used by a C++ program, which could set a throwing global
>> violation handler. Looking at the problem from another side: A global
>> violation handler is not a orthogonal abstraction and conflicts with
>> real-world use (i.e. libraries). I honestly do not understand why the C++
>> committee thinks that adding global state in 2023 is a viable solution to
>> any problem."
>>
>> I am wondering if there is a good counter-argument to this concern? Having
>> asserts in the middle of a function somewhere deep in the library suddenly
>> throw an exception, if the surrounding code has never been designed with
>> exception-safety in mind, sounds like a genuine concern for which I can't
>> think of a good solution right now.
>>
>> The only thing that comes to mind is the line of arguing where you say
>> that if you run into a failing assert, you're basically toast anyway, so it
>> doesn't matter if the violation handler screws up all the invariants in
>> your library by throwing. But somehow that doesn't sound super convincing.
>> Unless we teach that failing asserts are not supposed to be recoverable
>> (but on the other hand, we now also have the "observe" semantic).
>>
>> Thoughts?
We should teach that a "sufficiently smart" compiler would evaluate your program, and either fail to compile because there exists a code path that would violate the contract; or because no such path exists it would optimize out the contract violation handler as not reachable code. Since we don't have such a compiler, we allow contract violation handlers so you can clean up after encountering a serious bus.
Because of the above I think the question is one that we are safe to ignore - while the issue is real, it "should" never happen. They should write their library such that they don't have contract violations on any valid input. (in their interfaces they may either check for valid inputs like everyone "is supposed to", and return an error, or they can put in a contract on their interfaces and the violation handler will be invoked before getting into their code). Of course if they do have an internal error and violate their own internal contracts an exception will be thrown, but they are already in trouble: the user will need to contact tech support either way, hopefully they have enough logs to find/fix the original contract violation.
There is also the possibly the contract violation is in some library they in turn call, but we can assume they already have something to ensure the libraries they call don't throw an exception, and they call those libraries with valid inputs.
> Thanks Timur. I took the liberty of adding SG14 to the conversation as I
> think they might have some interest in this specific topic.
>
> Le lun. 3 juil. 2023 à 15:59, Timur Doumler via SG21 <sg21_at_[hidden]>
> a écrit :
>
>> Hello SG21,
>>
>> Here's an issue with the current Contracts design that was brought to my
>> attention by a user from the music technology industry, I am curious what
>> y'all think about it.
>>
>> They are very concerned that failing contract checks, and in particular
>> failing *asserts*, which can appear in the middle of a function body, can
>> call a user-defined violation handler which can be defined to throw an
>> exception. They say that basically this means that anywhere they currently
>> have an assert somewhere deep in their library, the user can turn that
>> assert into a throwing function, without the library knowing about it. So
>> they would have to go off and rewrite all their library code to be
>> exception-safe, which is an unacceptable burden to them.
>>
>> Here's a line of code with an assert in the OpenMPT library:
>> https://github.com/OpenMPT/openmpt/blob/b6a3124344de885f08286ac17880e31247db882b/soundlib/Fastmix.cpp#L184
>>
>>
>> Here's another assert in the same library:
>>
>> https://github.com/OpenMPT/openmpt/blob/b6a3124344de885f08286ac17880e31247db882b/soundlib/Sndfile.cpp#L1874
>>
>>
>> ...which is being called from here:
>>
>> https://github.com/OpenMPT/openmpt/blob/b6a3124344de885f08286ac17880e31247db882b/soundlib/Snd_fx.cpp#L1002
>>
>>
>> This is what the user is saying:
>>
>> "I honestly do not want to invest even a single second thinking about what
>> state any internal data structures are in when these lines suddenly throw.
>> That's not a part of code where we expect any exception, but when migrating
>> the assertions to contracts, we would have to deal with any assertion
>> potentially throwing. I could document that we do not support throwing
>> contract violation handlers (which implies not supporting all situation
>> specified by the C++ standard, and feels wrong), but then a C library that
>> calls into our C++ library (supported, because we provide a facade C
>> interface) would also need to document this, because this C library itself
>> could end up being used by a C++ program, which could set a throwing global
>> violation handler. Looking at the problem from another side: A global
>> violation handler is not a orthogonal abstraction and conflicts with
>> real-world use (i.e. libraries). I honestly do not understand why the C++
>> committee thinks that adding global state in 2023 is a viable solution to
>> any problem."
>>
>> I am wondering if there is a good counter-argument to this concern? Having
>> asserts in the middle of a function somewhere deep in the library suddenly
>> throw an exception, if the surrounding code has never been designed with
>> exception-safety in mind, sounds like a genuine concern for which I can't
>> think of a good solution right now.
>>
>> The only thing that comes to mind is the line of arguing where you say
>> that if you run into a failing assert, you're basically toast anyway, so it
>> doesn't matter if the violation handler screws up all the invariants in
>> your library by throwing. But somehow that doesn't sound super convincing.
>> Unless we teach that failing asserts are not supposed to be recoverable
>> (but on the other hand, we now also have the "observe" semantic).
>>
>> Thoughts?
We should teach that a "sufficiently smart" compiler would evaluate your program, and either fail to compile because there exists a code path that would violate the contract; or because no such path exists it would optimize out the contract violation handler as not reachable code. Since we don't have such a compiler, we allow contract violation handlers so you can clean up after encountering a serious bus.
Because of the above I think the question is one that we are safe to ignore - while the issue is real, it "should" never happen. They should write their library such that they don't have contract violations on any valid input. (in their interfaces they may either check for valid inputs like everyone "is supposed to", and return an error, or they can put in a contract on their interfaces and the violation handler will be invoked before getting into their code). Of course if they do have an internal error and violate their own internal contracts an exception will be thrown, but they are already in trouble: the user will need to contact tech support either way, hopefully they have enough logs to find/fix the original contract violation.
There is also the possibly the contract violation is in some library they in turn call, but we can assume they already have something to ensure the libraries they call don't throw an exception, and they call those libraries with valid inputs.
Received on 2023-07-04 01:47:39