logical const without casts!

Steven Schveighoffer schveiguy at yahoo.com
Fri Sep 30 12:12:59 PDT 2011


On Fri, 30 Sep 2011 14:51:19 -0400, Christophe  
<travert at phare.normalesup.org> wrote:

> "Steven Schveighoffer" , dans le message (digitalmars.D:145850), a

>> This also doesn't work:
> Indeed. The compiler should not allow the implicit conversion of
> globalFoo to immutable.
>
>> class Foo
>> {
>>     this(ref int _i) { dg = () { return _i; } }
>>     ref const(int) delegate() const dg;
>>     ref const(int) i() const { return dg(); }
>> }
>>
>> int globalInt;
>>
>> immutable Foo globalFoo = Foo(globalInt);
> ^ error: can't cast ref int _i to immutable ref int in immutable
> Foo.this()._delegate_1.

But ref int _i is the parameter to the constructor.  Here is where your  
proposal breaks down.

It's entirely feasible for class Foo to be compiled *separately* from the  
module that contains globalFoo.  And it's also possible that when  
compiling the above line, the language *does not have* access to the  
source code of the constructor.  How does it know that _i gets put into a  
const delegate?

Likewise, when compiling Foo, how does the compiler know that the  
constructor is being used to cast to immutable?  I just don't think there  
is enough information for the compiler to make the correct decision.

>
>> void thread1()
>> {
>>     writeln(globalFoo.i);
>> }
>>
>> int globalBar()
>> {
>>     spawn(&thread1);
>>     globalInt = 5; // does thread1 see this change or not?
>> }
>
>> It looks like any time you call a delegate member of an immutable  
>> object,
>> it could access a context pointer that is not implicitly shared.
>
> Not if the compiler cares to check the delegate context is indeed
> immutable when the immutable delegate is created. It is not much more
> complicated than if Foo contained an explicit pointer to i instead of a
> delegate

What if it has no access to the delegate creation code?  How does it  
disallow it?

>> Not to mention, what the hell does a const function mean for a delegate
>> literal?  The context pointer is the context of the function, not an
>> object.  How does that even work?
>
> Internally, it is a pointer to a structure containing the delegate
> context that is generated by the compiler. If the delegate is const, the
> pointer should be a const pointer. If the delegate is immutable, it
> should be an immutable pointer, and the compiler as to check there is no
> mutable version of that pointer before making an implicit cast.

Hm.. I don't know if there's a way to create such a delegate.  I'd have to  
check.

>> What is probably the "right" solution is to disallow implicit immutable
>> objects which have a delegate.  This means:
>>
>> a) you cannot initialize a shared or immutable such object without a
>> cast.  This means the line immutable Foo globalFoo = new Foo(globalInt)  
>> is
>> an error without a cast.
>
> Agreed, but with my proposal, you can have an immutable Foo, if the
> delegate context pointer can be cast to immutable.

You can also have an immutable Foo if you cast it to immutable.  For  
example, this line should compile:

immutable foo = cast(immutable(Foo))new Foo(globalInt);

But then the onus is on you to ensure you are doing the right thing.  So  
in both solutions, it's possible.  While I don't think your solution is  
feasible or solves the problem you set out to solve, if you did find a way  
to make the compiler make more guarantees, it would be better.  I just  
don't know if it's worth the extra syntax and restrictions.

>> b) Any time you have a const object that contains a delegate, it can be
>> assumed that the object is not shared.
>
> If the object is const, you have no guarantee that the objet is not
> shared, making an exception for object containing a delegate is quite
> wierd.

Well, since you cannot implicitly create such an immutable object, it  
could be an assumption you can make.  However, I can see why it wouldn't  
be a good idea to make that assumption (especially if you created one via  
casting).

We can probably drop this assumption, because to write code like the  
above, even if explicitly casting, you are asking for race conditions.   
Probably safe to assume that it works just like any other const object.

>
>> And then we avoid dealing with the const delegate issue altogether.
>
> Dealing with this issue could be very useful for multithreaded
> applications.
>
>>> With my proposal, you can very easily keep an immutable reference in a
>>> const delegate. The delegate will just not be callable if its
>>> function pointer is not const with regard to the context pointer.
>>
>> You can also very easily keep a mutable reference in a const delegate
>> inside an immutable object.  It's not mutable through the delegate, but
>> it's also not immutable.
>
> No, the compiler should check the delegate context is immutable when
> making the cast to immutable.

As I said above, it's not always possible to check the context.

-Steve


More information about the Digitalmars-d mailing list