Thread overview
fake RefCounted for CTFE?
Jun 15, 2020
Timon Gehr
Jun 15, 2020
Stanislav Blinov
Jun 16, 2020
tsbockman
Jun 16, 2020
John Colvin
Jun 16, 2020
Stanislav Blinov
June 15, 2020
I'm doing some stuff with RefCounted for a factory-like expression, where I expect the factory harnesses are going to be very short-lived. However, these don't work at CTFE (because malloc/etc aren't available).

Would it make sense for RefCounted to turn into GC for CTFE mode? That is, if RefCounted is used during CTFE, it just allocates on the GC heap and doesn't actually do ref counting. The end result will be a ref counted struct that somehow is flagged that it's never going away (and it shouldn't, if it's generated at compile time).

Then you could use functions that return RefCounted items as static initializers, or use libraries that use RefCounted at compile time without issues.

What do you think?

-Steve
June 15, 2020
On 15.06.20 23:18, Steven Schveighoffer wrote:
> I'm doing some stuff with RefCounted for a factory-like expression, where I expect the factory harnesses are going to be very short-lived. However, these don't work at CTFE (because malloc/etc aren't available).
> 
> Would it make sense for RefCounted to turn into GC for CTFE mode? That is, if RefCounted is used during CTFE, it just allocates on the GC heap and doesn't actually do ref counting. The end result will be a ref counted struct that somehow is flagged that it's never going away (and it shouldn't, if it's generated at compile time).
> 
> Then you could use functions that return RefCounted items as static initializers, or use libraries that use RefCounted at compile time without issues.
> 
> What do you think?
> 
> -Steve

I think it should work in CTFE, but it cannot break @nogc.
June 15, 2020
On Monday, 15 June 2020 at 21:18:23 UTC, Steven Schveighoffer wrote:

> a ref counted struct that somehow is flagged that it's never going away

Something just doesn't seem quite convincing about that proposition ;)

June 16, 2020
On Monday, 15 June 2020 at 21:18:23 UTC, Steven Schveighoffer wrote:
> Would it make sense for RefCounted to turn into GC for CTFE mode? That is, if RefCounted is used during CTFE, it just allocates on the GC heap and doesn't actually do ref counting. ...
> What do you think?

Ref counting isn't only for memory management - sometimes it's used to ensure that a value's destructor will run at a logical point in time. Using the garbage collector during CTFE instead of malloc/free or whatever should be fine, but some thought would need to be put into whether there are any other valid uses of destructors during CTFE.

The requirement that CTFE functions be pure at least forbids *most* other uses of destructors - for example, there is no need to close a file in CTFE, since you can't open one to begin with.

But, does D's weak purity provably forbid *all* other valid uses of destructors? Making a bad assumption here could break code in really ugly and confusing ways, since CTFE is often triggered implicitly without any deliberate intent on the part of the programmer.
June 15, 2020
On 6/15/20 5:39 PM, Timon Gehr wrote:
> On 15.06.20 23:18, Steven Schveighoffer wrote:
>> I'm doing some stuff with RefCounted for a factory-like expression, where I expect the factory harnesses are going to be very short-lived. However, these don't work at CTFE (because malloc/etc aren't available).
>>
>> Would it make sense for RefCounted to turn into GC for CTFE mode? That is, if RefCounted is used during CTFE, it just allocates on the GC heap and doesn't actually do ref counting. The end result will be a ref counted struct that somehow is flagged that it's never going away (and it shouldn't, if it's generated at compile time).
>>
>> Then you could use functions that return RefCounted items as static initializers, or use libraries that use RefCounted at compile time without issues.
>>
>> What do you think?
> 
> I think it should work in CTFE, but it cannot break @nogc.

Damn you're right. I can't think of a good way around that.

All I can think of is a specialized function which allocates only in CTFE, and the compiler pretends it can be @nogc.

-Steve
June 15, 2020
On 6/15/20 7:08 PM, Stanislav Blinov wrote:
> On Monday, 15 June 2020 at 21:18:23 UTC, Steven Schveighoffer wrote:
> 
>> a ref counted struct that somehow is flagged that it's never going away
> 
> Something just doesn't seem quite convincing about that proposition ;)
> 

In essence there's no reason to ref-count, because it lives in static data-land.

-Steve
June 15, 2020
On 6/15/20 8:03 PM, tsbockman wrote:
> On Monday, 15 June 2020 at 21:18:23 UTC, Steven Schveighoffer wrote:
>> Would it make sense for RefCounted to turn into GC for CTFE mode? That is, if RefCounted is used during CTFE, it just allocates on the GC heap and doesn't actually do ref counting. ...
>> What do you think?
> 
> Ref counting isn't only for memory management - sometimes it's used to ensure that a value's destructor will run at a logical point in time. Using the garbage collector during CTFE instead of malloc/free or whatever should be fine, but some thought would need to be put into whether there are any other valid uses of destructors during CTFE.

Yeah, this is a good point.

It could still run the destructor, but not free the memory. So instead of marking it as not truly ref-counted, just mark it as not needing to be freed.

-Steve
June 15, 2020
On 6/15/20 8:23 PM, Steven Schveighoffer wrote:
> On 6/15/20 5:39 PM, Timon Gehr wrote:
>> On 15.06.20 23:18, Steven Schveighoffer wrote:
>>> I'm doing some stuff with RefCounted for a factory-like expression, where I expect the factory harnesses are going to be very short-lived. However, these don't work at CTFE (because malloc/etc aren't available).
>>>
>>> Would it make sense for RefCounted to turn into GC for CTFE mode? That is, if RefCounted is used during CTFE, it just allocates on the GC heap and doesn't actually do ref counting. The end result will be a ref counted struct that somehow is flagged that it's never going away (and it shouldn't, if it's generated at compile time).
>>>
>>> Then you could use functions that return RefCounted items as static initializers, or use libraries that use RefCounted at compile time without issues.
>>>
>>> What do you think?
>>
>> I think it should work in CTFE, but it cannot break @nogc.
> 
> Damn you're right. I can't think of a good way around that.
> 
> All I can think of is a specialized function which allocates only in CTFE, and the compiler pretends it can be @nogc.

Another suggestion I found on the issue tracker:

https://issues.dlang.org/show_bug.cgi?id=18119

-Steve
June 16, 2020
On Monday, 15 June 2020 at 21:18:23 UTC, Steven Schveighoffer wrote:
> I'm doing some stuff with RefCounted for a factory-like expression, where I expect the factory harnesses are going to be very short-lived. However, these don't work at CTFE (because malloc/etc aren't available).
>
> Would it make sense for RefCounted to turn into GC for CTFE mode? That is, if RefCounted is used during CTFE, it just allocates on the GC heap and doesn't actually do ref counting. The end result will be a ref counted struct that somehow is flagged that it's never going away (and it shouldn't, if it's generated at compile time).
>
> Then you could use functions that return RefCounted items as static initializers, or use libraries that use RefCounted at compile time without issues.
>
> What do you think?
>
> -Steve

why not just have it continue doing ref-counting but use the GC to do the allocating? Can't use GC.malloc at ctfe but `new ubyte[](N)` works fine. You could even do a lambda cast to trick it in to being nogc.
June 16, 2020
On Tuesday, 16 June 2020 at 13:36:28 UTC, John Colvin wrote:

> why not just have it continue doing ref-counting but use the GC to do the allocating? Can't use GC.malloc at ctfe but `new ubyte[](N)` works fine. You could even do a lambda cast to trick it in to being nogc.

You can't. RefCounted (in Phobos, and any sane implementation otherwise) performs operations that simply aren't supported by CTFE. You can't do reinterpret casts in CTFE. You can't `new` a type with disabled default constructor. You can't properly initialize it if it has overridden opAssign.

This is due to the sad deficiency of the language in that it doesn't define a form of placement new. There's a library solution, but it won't work in CTFE generically for the above reasons.