D does have head const (but maybe it shouldn't)

ag0aep6g anonymous at example.com
Tue Dec 29 19:22:49 UTC 2020


On Tuesday, 29 December 2020 at 17:56:51 UTC, H. S. Teoh wrote:
> On Tue, Dec 29, 2020 at 05:13:48PM +0000, Petar via 
> Digitalmars-d wrote:
>> On Tuesday, 29 December 2020 at 16:15:56 UTC, ag0aep6g wrote:
[...]
>> > The validity of the given solution might be arguable, and 
>> > I'd be in favour of outlawing it. It's surprising that 
>> > there's a type for which `const` means head const when it 
>> > means transitive const for everything else. It's so 
>> > surprising that even DMD trips over it 
>> > (<https://issues.dlang.org/show_bug.cgi?id=21511>).
>> 
>> Wow. The solution surprised me, even though in retrospect it's 
>> obvious to me why it works (bugs in this area of the type 
>> system have been know for more than a few years). I'd say 
>> head-const is a very useful tool, but it's pretty obvious to 
>> me that this shouldn't be a supported way to implement it.
>
> I don't see why this is considered head const, nor why this 
> should be considered a bug. In the proposed solution `y` is not 
> a value type but a delegate that wraps the reference to *x.  
> Since x itself is non-const, this is perfectly valid, and the 
> `const` in `const y` refers to the immutability of the delegate 
> reference, not to the immutability of the wrapped reference.

How are you not just describing head const? A thing that is 
itself const and refers to something mutable => head const.

You know how a dynamic array (or slice) is often described as a 
pointer plus a length. Explanations to newbies often contain a 
mockup struct like this:

struct Slice(E)
{
     E* ptr;
     size_t length;
}

Because that's what a slice is, right? And when the newbie starts 
to think about it that way, things often fall into place for them.

Similarly, it makes sense to think of a delegate like this:

struct Delegate(C, R, P ...)
{
     C* context_ptr;
     R function(C* context_ptr, P params) funcptr;
     R opCall(P params) { return funcptr(context_ptr, params); }
}

(Ignoring details like how the context pointer is actually 
passed.)

Now, when you have a `const Delegate!(int, void, /* no parameters 
*/)`, its `context_ptr` is also const and you cannot gain mutable 
access to the referenced int. Because that's how `const` works in 
D (for the most part). But that restriction does not apply to a 
`const void delegate()` whose context pointer is an `int*`.

I think it would save us some headaches if delegates behaved like 
the structs that they really are.


More information about the Digitalmars-d mailing list