June 10, 2021
On 6/9/21 11:13 PM, Vladimir Panteleev wrote:
> On Wednesday, 9 June 2021 at 14:01:39 UTC, Steven Schveighoffer wrote:
>> On 6/9/21 7:02 AM, Steven Schveighoffer wrote:
>>> Come to think of it, an InvalidMemoryOperationError should use malloc instead of GC, then maybe we can get stack traces?
>>
>> I delved into this a bit, trying to see where the GC allocations are happening. There are some functions that are not marked nogc that can be.
>>
>> I got hung up on core.demangle.demangle.
>>
>> It's used here: https://github.com/dlang/druntime/blob/751f5665a31ea0f150d71b22a9852abacc2f61ec/src/core/runtime.d#L830-L865 
>>
> 
> Would it be possible to just skip demangling in situations where the GC is unavailable?
> 
> Pasting the output through [ddemangle](https://github.com/dlang/tools/blob/master/ddemangle.d) isn't hard at all.
> 

Yeah, actually, that's a good idea. It's better than no stack trace, and with GC.inFinalizer, we have the ability to know when we can use demangle or not in the general case. A further update can fix the demangler, and then the printouts just get better.

Since this is all part of the traceinfo, just fixing the traceinfo for that exception (which is custom anyway) is possible for now.

I'll see if I can make a PR.

-Steve
June 10, 2021
On Wednesday, 9 June 2021 at 17:54:06 UTC, Timon Gehr wrote:
> On 09.06.21 04:37, Walter Bright wrote:
>> 
>> Allocating via the GC is in assert is just ridiculous, as the program is going to exit shortly anyway. There won't ever be a need to run a collection on it. Just malloc away and fuggeddabootet.
>
> AssertError is still *caught* /by the language/ on in contract inheritance.

I'd say, lower assert failures in contracts to a different function, say, _d_contractFailed(), which can do a different thing, say, throw ContractError, and child contract can catch only this specific ContractError instead of any Error.
June 10, 2021

On Wednesday, 9 June 2021 at 17:54:06 UTC, Timon Gehr wrote:

>

On 09.06.21 04:37, Walter Bright wrote:

>

Allocating via the GC is in assert is just ridiculous, as the program is going to exit shortly anyway. There won't ever be a need to run a collection on it. Just malloc away and fuggeddabootet.

AssertError is still caught /by the language/ on in contract inheritance.

Note that -preview=inclusiveincontracts changes the semantics so that AssertError is, while still caught, never swallowed but at most converted into another Error. In other words, if the parent in-condition throws, the child in-condition will always throw as well.

For discussion of this feature, see https://github.com/dlang/dmd/pull/11465 .

I'm sorry I haven't had time to push on this recently.

June 10, 2021

On Thursday, 10 June 2021 at 12:49:19 UTC, FeepingCreature wrote:

>

Note that -preview=inclusiveincontracts changes the semantics so that AssertError is, while still caught, never swallowed but at most converted into another Error. In other words, if the parent in-condition throws, the child in-condition will always throw as well.

For discussion of this feature, see https://github.com/dlang/dmd/pull/11465 .

Sorry, let me correct that and explain a bit more.

The semantics change of "inclusive in-contracts" is that the in-contract of the child has to include the in-contract of the parent in the course of expanding it. In other words, you can't override foo(int i) in (i == 2) with foo(int i) in (i == 3), but only with foo(int i) in (i == 2 || i == 3). This is validated by first checking the child contract, then if it fails, recursively checking that the parent also failed. In other words, the child's error is caught, but the handler then either throws the parent's error or a LogicError.

So with this change, assert can violate @nogc as it wants, because the program cannot get out of throwing an Error one way or another.

June 10, 2021
On 6/10/21 8:07 AM, Kagamin wrote:
> On Wednesday, 9 June 2021 at 17:54:06 UTC, Timon Gehr wrote:
>> On 09.06.21 04:37, Walter Bright wrote:
>>>
>>> Allocating via the GC is in assert is just ridiculous, as the program is going to exit shortly anyway. There won't ever be a need to run a collection on it. Just malloc away and fuggeddabootet.
>>
>> AssertError is still *caught* /by the language/ on in contract inheritance.
> 
> I'd say, lower assert failures in contracts to a different function, say, _d_contractFailed(), which can do a different thing, say, throw ContractError, and child contract can catch only this specific ContractError instead of any Error.

What about asserts in functions called by the contract? I guess technically they are not part of the contract, just poor coding, but people may rely on those asserts in some cases.

-Steve
June 10, 2021
On Thursday, 10 June 2021 at 12:59:59 UTC, Steven Schveighoffer wrote:
> On 6/10/21 8:07 AM, Kagamin wrote:
>> On Wednesday, 9 June 2021 at 17:54:06 UTC, Timon Gehr wrote:
>>> On 09.06.21 04:37, Walter Bright wrote:
>>>>
>>>> Allocating via the GC is in assert is just ridiculous, as the program is going to exit shortly anyway. There won't ever be a need to run a collection on it. Just malloc away and fuggeddabootet.
>>>
>>> AssertError is still *caught* /by the language/ on in contract inheritance.
>> 
>> I'd say, lower assert failures in contracts to a different function, say, _d_contractFailed(), which can do a different thing, say, throw ContractError, and child contract can catch only this specific ContractError instead of any Error.
>
> What about asserts in functions called by the contract? I guess technically they are not part of the contract, just poor coding, but people may rely on those asserts in some cases.
>
> -Steve

All the more reason to throw `ContractError` instead of `AssertError`. Asserts in an unrelated function should not be caught as part of in-condition processing.
June 10, 2021
On 6/10/21 9:09 AM, FeepingCreature wrote:
> On Thursday, 10 June 2021 at 12:59:59 UTC, Steven Schveighoffer wrote:
>> On 6/10/21 8:07 AM, Kagamin wrote:
>>> On Wednesday, 9 June 2021 at 17:54:06 UTC, Timon Gehr wrote:
>>>> On 09.06.21 04:37, Walter Bright wrote:
>>>>>
>>>>> Allocating via the GC is in assert is just ridiculous, as the program is going to exit shortly anyway. There won't ever be a need to run a collection on it. Just malloc away and fuggeddabootet.
>>>>
>>>> AssertError is still *caught* /by the language/ on in contract inheritance.
>>>
>>> I'd say, lower assert failures in contracts to a different function, say, _d_contractFailed(), which can do a different thing, say, throw ContractError, and child contract can catch only this specific ContractError instead of any Error.
>>
>> What about asserts in functions called by the contract? I guess technically they are not part of the contract, just poor coding, but people may rely on those asserts in some cases.
>>
> 
> All the more reason to throw `ContractError` instead of `AssertError`. Asserts in an unrelated function should not be caught as part of in-condition processing.

What about asserts in a related function? The foundation of programming with functions is that you can abstract common implementation into another function.

I could easily see someone making a helper function for lots of similar contracts.

It's an easy fix, just use to-be-implemented "contract_assert" which throws the right error, but the change would break code that exists. A deprecation might be painful (probably only triggered at runtime, so you might not catch all your cases).

-Steve
June 10, 2021
On Thursday, 10 June 2021 at 14:07:04 UTC, Steven Schveighoffer wrote:
> What about asserts in a related function? The foundation of programming with functions is that you can abstract common implementation into another function.
>
> I could easily see someone making a helper function for lots of similar contracts.

Maybe move all that logic into the precondition of that function?

void businessFunction(a,b,c) in { checkContract(a,b,c); } {...}
void checkContract(a,b,c) in { lots of asserts } { empty }
June 10, 2021
On Thursday, 10 June 2021 at 12:07:34 UTC, Kagamin wrote:
>
> I'd say, lower assert failures in contracts to a different function, say, _d_contractFailed(), which can do a different thing, say, throw ContractError, and child contract can catch only this specific ContractError instead of any Error.

This could also enable a different use case: Testing functions with random inputs, but ignoring failures for invalid inputs.

void randomTest(alias F)()
{
    while(true)
    {
        Parameters!F args;
        generateRandomArgs!(args);
        try
        {
            F(args);
        }
        catch(ContractError)
        {
            // Ignore
        }
        catch(Exception)
        {
            // Ignore
        }
    }
}

This could find bugs, where an assert or bounds check fails in the function.
June 10, 2021
On 6/10/21 1:09 PM, Kagamin wrote:
> On Thursday, 10 June 2021 at 14:07:04 UTC, Steven Schveighoffer wrote:
>> What about asserts in a related function? The foundation of programming with functions is that you can abstract common implementation into another function.
>>
>> I could easily see someone making a helper function for lots of similar contracts.
> 
> Maybe move all that logic into the precondition of that function?
> 
> void businessFunction(a,b,c) in { checkContract(a,b,c); } {...}
> void checkContract(a,b,c) in { lots of asserts } { empty }

You're not wrong, but it doesn't fix existing code which might not have done it that way.

-Steve