December 15, 2022
On Thursday, 15 December 2022 at 13:30:57 UTC, Siarhei Siamashka wrote:
> Does it also GC allocate behind the scene?

Is the stack trace correct? If so, yes it does, but I think here since it is static immutable you'll just have an empty stack trace... but im not sure i shuold try running it but im lazy.

> Are there any other possible problems with it?

Yes, a lot.

1) `immutable` is ignored on exceptions. The implementation casts it away internally as it is thrown, so when it is caught, it is mutable again. If someone tried to actually modify it at this point, it is undefined behavior. (in this case i think the implementation would just retain the edits for another call to the same enforce msg, T arguments)

2) Similarly to #1, if someone were to catch then escape the reference to the exception (which is done by `core.thread.Thread.join` for example) they'd possibly find it changing out from under them.

Since it is marked `static immutable` this doesn't apply, but if it wasn't `immutable` at construction, it would be a thread-local variable which is, again, crash city if someone were to `Thread.join` since it'd be use-after-free then by definition (the thread local block is destroyed along with the thread, and thread.join terminates the thread). BTW this is also dip1008's core problem.

However, being a global instance, if two threads were to throw it simultaneously... this ties back to the immutable being casted away, but possible corruption to the data there too.


static exceptions work for simple cases but there's a number of pitfalls using them beyond the basics, especially when threads come into play.
December 15, 2022

On Thursday, 15 December 2022 at 13:45:03 UTC, Adam D Ruppe wrote:

>
  1. immutable is ignored on exceptions. The implementation casts it away internally as it is thrown, so when it is caught, it is mutable again. If someone tried to actually modify it at this point, it is undefined behavior. (in this case i think the implementation would just retain the edits for another call to the same enforce msg, T arguments)

I was thinking to add a disabled default constructor for Throwable:

@disable immutable this();

But then every subclass of Throwable has to do the same. What if subclasses inherited a disabled constructor - that might solve the problem?

December 15, 2022

On Wednesday, 14 December 2022 at 01:47:29 UTC, Steven Schveighoffer wrote:

>

But if the string you give it happens to not contain a string representation of an integer, it wants to throw an exception. And the act of allocating and throwing that exception needs the GC.

We really really need to fix it. It completely cuts the legs out of the answer "if you don't want the gc, use @nogc". If we do fix it, all these questions pretty much just go away. It goes from something like 20% of phobos being nogc-compatible to 80%.

Is avoiding the GC inside exceptions a problem that needs to be solved? Maybe it is, but I don't think it's common to have a loop with millions of exceptions. Perhaps the issue is that there should be a version of @nogc that doesn't care about exceptions. With the current implementation of exceptions, the intersection of "avoiding GC" and "abusing exceptions" is almost certainly small.

December 15, 2022

On 12/15/22 6:04 PM, bachmeier wrote:

>

On Wednesday, 14 December 2022 at 01:47:29 UTC, Steven Schveighoffer wrote:

>

But if the string you give it happens to not contain a string representation of an integer, it wants to throw an exception. And the act of allocating and throwing that exception needs the GC.

We really really need to fix it. It completely cuts the legs out of the answer "if you don't want the gc, use @nogc". If we do fix it, all these questions pretty much just go away. It goes from something like 20% of phobos being nogc-compatible to 80%.

Is avoiding the GC inside exceptions a problem that needs to be solved? Maybe it is, but I don't think it's common to have a loop with millions of exceptions. Perhaps the issue is that there should be a version of @nogc that doesn't care about exceptions. With the current implementation of exceptions, the intersection of "avoiding GC" and "abusing exceptions" is almost certainly small.

Why does the quantity of exceptions matter? The point of avoiding the GC is to avoid the collection, which can happen with a single allocation. If you want to avoid the GC for specific code paths, you don't want to say "OK, I guess I can't parse integers in here".

Note that it's also possible to assume the GC likely won't get triggered, because an exception is very unlikely. But having a mechanism to ask the compiler to help prove it, which can't be used, is pretty frustrating.

-Steve

December 16, 2022

On Thursday, 15 December 2022 at 23:29:55 UTC, Steven Schveighoffer wrote:

>

On 12/15/22 6:04 PM, bachmeier wrote:

>

On Wednesday, 14 December 2022 at 01:47:29 UTC, Steven Schveighoffer wrote:

>

But if the string you give it happens to not contain a string representation of an integer, it wants to throw an exception. And the act of allocating and throwing that exception needs the GC.

We really really need to fix it. It completely cuts the legs out of the answer "if you don't want the gc, use @nogc". If we do fix it, all these questions pretty much just go away. It goes from something like 20% of phobos being nogc-compatible to 80%.

Is avoiding the GC inside exceptions a problem that needs to be solved? Maybe it is, but I don't think it's common to have a loop with millions of exceptions. Perhaps the issue is that there should be a version of @nogc that doesn't care about exceptions. With the current implementation of exceptions, the intersection of "avoiding GC" and "abusing exceptions" is almost certainly small.

Why does the quantity of exceptions matter? The point of avoiding the GC is to avoid the collection, which can happen with a single allocation. If you want to avoid the GC for specific code paths, you don't want to say "OK, I guess I can't parse integers in here".

It matters if you expect the quantity to be zero. If you've thoroughly tested your code, and you're confident that the exception isn't relevant, it doesn't matter. It is quite rare that to!int would throw an exception in my code.

>

Note that it's also possible to assume the GC likely won't get triggered, because an exception is very unlikely. But having a mechanism to ask the compiler to help prove it, which can't be used, is pretty frustrating.

Those wanting that could continue to use the current @nogc. If they're fine with a one in a million chance of a collection, they don't need that kind of proof, and they could use @nogc right now with the 80% of Phobos you have cited.

December 15, 2022

On 12/15/22 7:27 PM, bachmeier wrote:

>

On Thursday, 15 December 2022 at 23:29:55 UTC, Steven Schveighoffer wrote:

>

On 12/15/22 6:04 PM, bachmeier wrote:

>

On Wednesday, 14 December 2022 at 01:47:29 UTC, Steven Schveighoffer wrote:

>

But if the string you give it happens to not contain a string representation of an integer, it wants to throw an exception. And the act of allocating and throwing that exception needs the GC.

We really really need to fix it. It completely cuts the legs out of the answer "if you don't want the gc, use @nogc". If we do fix it, all these questions pretty much just go away. It goes from something like 20% of phobos being nogc-compatible to 80%.

Is avoiding the GC inside exceptions a problem that needs to be solved? Maybe it is, but I don't think it's common to have a loop with millions of exceptions. Perhaps the issue is that there should be a version of @nogc that doesn't care about exceptions. With the current implementation of exceptions, the intersection of "avoiding GC" and "abusing exceptions" is almost certainly small.

Why does the quantity of exceptions matter? The point of avoiding the GC is to avoid the collection, which can happen with a single allocation. If you want to avoid the GC for specific code paths, you don't want to say "OK, I guess I can't parse integers in here".

It matters if you expect the quantity to be zero. If you've thoroughly tested your code, and you're confident that the exception isn't relevant, it doesn't matter. It is quite rare that to!int would throw an exception in my code.

Totally agreed. But one to!int inside a big function makes it so it can't be @nogc. You might want the @nogc for other reasons. It just strikes me as limiting that converting string to int cancels the ability to use @nogc at all.

> >

Note that it's also possible to assume the GC likely won't get triggered, because an exception is very unlikely. But having a mechanism to ask the compiler to help prove it, which can't be used, is pretty frustrating.

Those wanting that could continue to use the current @nogc. If they're fine with a one in a million chance of a collection, they don't need that kind of proof, and they could use @nogc right now with the 80% of Phobos you have cited.

Maybe you misunderstood what I said. 20% of phobos is usable with @nogc (not tested, but that's my expectation). Fixing exceptions so they don't use the GC would be a single thing that flips that 20% to 80%.

-Steve

December 15, 2022
Lot of common sense there.

We should publish "how to do it with malloc" so people can figure it out. Perhaps you can add it to the D wiki!
December 16, 2022

On Thursday, 15 December 2022 at 13:45:03 UTC, Adam D Ruppe wrote:

>
  1. immutable is ignored on exceptions. The implementation casts it away internally as it is thrown, so when it is caught, it is mutable again. If someone tried to actually modify it at this point, it is undefined behavior. (in this case i think the implementation would just retain the edits for another call to the same enforce msg, T arguments)

So I tried to mark this immutable data with VALGRIND_MAKE_MEM_NOACCESS and valgrind reports just a single write access to it from here: https://github.com/dlang/dmd/blob/v2.101.1/druntime/src/rt/deh.d#L46

Is it possible to construct the immutable exception data in a way that the cast(byte*) t !is typeid(t).initializer.ptr check would fail? What's the purpose of this check?

Another problem is illustrated by the example below:

T enforce(string msg, T)(T cond) {
    if (!cond) {
        static immutable e = new Exception(msg);
        throw e;
    }
    return cond;
}

void main() {
    try {
        enforce!"trigger an exception"(1 == 2);
    } catch (Exception e) {
        assert(0, "if we reach here, then it's probably a compiler bug");
    } catch (immutable Exception e) {
        // the proper place to handle it is here
        assert(e.msg == "trigger an exception");
    }
}

If it's an immutable exception, then the compiler should probably only catch it as immutable?

>

static exceptions work for simple cases but there's a number of pitfalls using them beyond the basics, especially when threads come into play.

Right now it doesn't look like there are way too many problems preventing immutable exceptions from becoming usable.

December 16, 2022
On 14/12/2022 2:47 PM, Steven Schveighoffer wrote:
> We really really need to fix it. It completely cuts the legs out of the answer "if you don't want the gc, use @nogc". If we do fix it, all these questions pretty much just go away. It goes from something like 20% of phobos being nogc-compatible to 80%.

*whistles*

https://github.com/rikkimax/DIPs/blob/value_type_exceptions/DIPs/DIP1xxx-RC.md
December 16, 2022

On Friday, 16 December 2022 at 02:59:18 UTC, Walter Bright wrote:

>

Lot of common sense there.

We should publish "how to do it with malloc" so people can figure it out. Perhaps you can add it to the D wiki!

It already is...

https://wiki.dlang.org/Memory_Management#Explicit_Class_Instance_Allocation

At best we can ask Beginner D tutorials/books to also refer to it

I think mike's blog post already does this though.

Yeah he does mention it: https://dlang.org/blog/2017/09/25/go-your-own-way-part-two-the-heap/

Even Ali's book mentions emplace, but not in the context of @nogc

Ali, if you're reading, perhaps this can be added?