www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - the const correctness of the this pointer

reply TM <ithink therefore.iam> writes:
Hello, 
I was dealing with delegates when I found that the const correctness of the
this pointer was 'lost' when accessing a member function of a class through a
temporary delegate. 

Here is an example:

class A{
    void f () {}
}

const A a = new A;
a.f() //error, this is normal.
auto g = &a.f;
g(); //no error. Is this normal ?

I may be wrong, but it seems to me that as the const property of the this
pointer is linked to the instance of the class (here 'a'), the delegate 'g'
should contain a const pointer to A (because 'a' is const).

More generally, I seems that there is no management of the const correctness of
the frame pointer of a delegate in D2

What about adding the const keyword for delegates, as we do for methods ? The
const property of the delegate's frame pointer would be part of its type.

Example: 

void f()
{
    int x;
    void g() const {
        x ++; //would produce an error since the type of g is [void delegate()
const]
   }
}

 
Any thoughts about this ?
Dec 27 2009
next sibling parent Jason House <jason.james.house gmail.com> writes:
TM Wrote:

 Hello, 
 I was dealing with delegates when I found that the const correctness of the
this pointer was 'lost' when accessing a member function of a class through a
temporary delegate. 
 
 Here is an example:
 
 class A{
     void f () {}
 }
 
 const A a = new A;
 a.f() //error, this is normal.
 auto g = &a.f;
 g(); //no error. Is this normal ?
 
 I may be wrong, but it seems to me that as the const property of the this
pointer is linked to the instance of the class (here 'a'), the delegate 'g'
should contain a const pointer to A (because 'a' is const).
Looks like something that should be entered into bugzilla
Dec 27 2009
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 27 Dec 2009 19:02:23 -0500, TM <ithink therefore.iam> wrote:

 Hello,
 I was dealing with delegates when I found that the const correctness of  
 the this pointer was 'lost' when accessing a member function of a class  
 through a temporary delegate.

 Here is an example:

 class A{
     void f () {}
 }

 const A a = new A;
 a.f() //error, this is normal.
 auto g = &a.f;
 g(); //no error. Is this normal ?

 I may be wrong, but it seems to me that as the const property of the  
 this pointer is linked to the instance of the class (here 'a'), the  
 delegate 'g' should contain a const pointer to A (because 'a' is const).

 More generally, I seems that there is no management of the const  
 correctness of the frame pointer of a delegate in D2

 What about adding the const keyword for delegates, as we do for methods  
 ? The const property of the delegate's frame pointer would be part of  
 its type.

 Example:

 void f()
 {
     int x;
     void g() const {
         x ++; //would produce an error since the type of g is [void  
 delegate() const]
    }
 }

 Any thoughts about this ?
This might be a difficult thing to fix, but it definitely *definitely* needs to be fixed. The problem is that a delegate stores a function pointer and a context pointer. However, it does not type the context pointer. For example, if you do this: import std.stdio; class A { void f() const {} } void main() { const A a = new A; a.f(); auto g = &a.f; writefln("%s", typeof(g).stringof); } You will get this: void delegate() The this pointer is hidden, and so is it's const decoration. I would expect to see: void delegate() const I'll file a bugzilla request for this. -Steve
Dec 28 2009
parent reply Jason House <jason.james.house gmail.com> writes:
Steven Schveighoffer Wrote:

 
 This might be a difficult thing to fix, but it definitely *definitely*  
 needs to be fixed.  The problem is that a delegate stores a function  
 pointer and a context pointer.  However, it does not type the context  
 pointer.  For example, if you do this:
 
 import std.stdio;
 
 class A
 {
      void f() const {}
 }
 
 void main()
 {
      const A a = new A;
      a.f();
      auto g = &a.f;
      writefln("%s", typeof(g).stringof);
 }
 
 You will get this:
 
 void delegate()
 
 The this pointer is hidden, and so is it's const decoration.  I would  
 expect to see:
 
 void delegate() const
 
 I'll file a bugzilla request for this.
 
 -Steve
I disagree. Once the delegate is passed off to some other region of code, why should that other code care that there's an object that might not get modified from using the delegate? Especially when you consider that which object or stack frame is not tracked by the type system. Pure delegates make sense to me though.
Dec 28 2009
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 28 Dec 2009 10:50:02 -0500, Jason House  
<jason.james.house gmail.com> wrote:

 Steven Schveighoffer Wrote:

 This might be a difficult thing to fix, but it definitely *definitely*
 needs to be fixed.  The problem is that a delegate stores a function
 pointer and a context pointer.  However, it does not type the context
 pointer.  For example, if you do this:

 import std.stdio;

 class A
 {
      void f() const {}
 }

 void main()
 {
      const A a = new A;
      a.f();
      auto g = &a.f;
      writefln("%s", typeof(g).stringof);
 }

 You will get this:

 void delegate()

 The this pointer is hidden, and so is it's const decoration.  I would
 expect to see:

 void delegate() const

 I'll file a bugzilla request for this.

 -Steve
I disagree. Once the delegate is passed off to some other region of code, why should that other code care that there's an object that might not get modified from using the delegate? Especially when you consider that which object or stack frame is not tracked by the type system. Pure delegates make sense to me though.
It could be part of the interface for a function that takes a delegate (i.e. I require that this delegate does not modify it's 'this' parameter). But I agree, there could be issues -- if you don't care what the delegate does (const or not), then how do you specify that? I suppose we need delegate contravariance at that point -- a delegate to a const function should implicitly casts to a delegate of a non-const function. See http://d.puremagic.com/issues/show_bug.cgi?id=3075 This might be a good reason to revive that enhancement. BTW, you also need to correctly mark shared functions, which *do* matter as far as delegates go, since you need to synchronize access to the 'this' pointer before calling the delegate. -Steve
Dec 28 2009
prev sibling parent reply TM <ithink therefore.iam> writes:
Jason House Wrote:

 Steven Schveighoffer Wrote:
 
 
 This might be a difficult thing to fix, but it definitely *definitely*  
 needs to be fixed.  The problem is that a delegate stores a function  
 pointer and a context pointer.  However, it does not type the context  
 pointer.  For example, if you do this:
 
 import std.stdio;
 
 class A
 {
      void f() const {}
 }
 
 void main()
 {
      const A a = new A;
      a.f();
      auto g = &a.f;
      writefln("%s", typeof(g).stringof);
 }
 
 You will get this:
 
 void delegate()
 
 The this pointer is hidden, and so is it's const decoration.  I would  
 expect to see:
 
 void delegate() const
 
 I'll file a bugzilla request for this.
 
 -Steve
I disagree. Once the delegate is passed off to some other region of code, why should that other code care that there's an object that might not get modified from using the delegate? Especially when you consider that which object or stack frame is not tracked by the type system. Pure delegates make sense to me though.
I disagree with you. To me, the loss of the frame pointer's const attribute looks like a cast. I do not expect to be allowed to call the above 'a.f' wihout casting a into a const A. And yet it is possible by using a delegate, which I think performs a hidden cast operation. I think Steven Schveighoffer is right. Moreover the const correctness of the this pointer is well managed as far as objects are concerned, so why not have the same behaviour with delegate ?
Dec 28 2009
parent reply Jason House <jason.james.house gmail.com> writes:
TM Wrote:

 Jason House Wrote:
 
 Steven Schveighoffer Wrote:
 
 
 This might be a difficult thing to fix, but it definitely *definitely*  
 needs to be fixed.  The problem is that a delegate stores a function  
 pointer and a context pointer.  However, it does not type the context  
 pointer.  For example, if you do this:
 
 import std.stdio;
 
 class A
 {
      void f() const {}
 }
 
 void main()
 {
      const A a = new A;
      a.f();
      auto g = &a.f;
      writefln("%s", typeof(g).stringof);
 }
 
 You will get this:
 
 void delegate()
 
 The this pointer is hidden, and so is it's const decoration.  I would  
 expect to see:
 
 void delegate() const
 
 I'll file a bugzilla request for this.
 
 -Steve
I disagree. Once the delegate is passed off to some other region of code, why should that other code care that there's an object that might not get modified from using the delegate? Especially when you consider that which object or stack frame is not tracked by the type system. Pure delegates make sense to me though.
I disagree with you. To me, the loss of the frame pointer's const attribute looks like a cast.
The quoted code (by Steve) above is not the same as yours. Both the variable and the called member are const. Invoking the delegate does not violate the const system. I did respond to your original example saying to file a bugzilla entry.
 I do not expect to be allowed to call the above 'a.f' wihout casting a into a
const A. And yet it is possible by using a delegate, which I think performs a
hidden cast operation.
 
Calling a const member function is allowed without casting from either thread-local mutable data or immutable data. Calling a non-const member function on a const object is what's illegal. That's what your original sample did. Constructing such a delegate (without casts) should be illegal and is a bug in dmd. Once a const-correct delegate is created, it should be usable like any other delegate.
Dec 28 2009
parent TM <ithink therefore.iam> writes:
Jason House Wrote:

 TM Wrote:
 
 Jason House Wrote:
 
 Steven Schveighoffer Wrote:
 
 
 This might be a difficult thing to fix, but it definitely *definitely*  
 needs to be fixed.  The problem is that a delegate stores a function  
 pointer and a context pointer.  However, it does not type the context  
 pointer.  For example, if you do this:
 
 import std.stdio;
 
 class A
 {
      void f() const {}
 }
 
 void main()
 {
      const A a = new A;
      a.f();
      auto g = &a.f;
      writefln("%s", typeof(g).stringof);
 }
 
 You will get this:
 
 void delegate()
 
 The this pointer is hidden, and so is it's const decoration.  I would  
 expect to see:
 
 void delegate() const
 
 I'll file a bugzilla request for this.
 
 -Steve
I disagree. Once the delegate is passed off to some other region of code, why should that other code care that there's an object that might not get modified from using the delegate? Especially when you consider that which object or stack frame is not tracked by the type system. Pure delegates make sense to me though.
I disagree with you. To me, the loss of the frame pointer's const attribute looks like a cast.
The quoted code (by Steve) above is not the same as yours. Both the variable and the called member are const. Invoking the delegate does not violate the const system. I did respond to your original example saying to file a bugzilla entry.
 I do not expect to be allowed to call the above 'a.f' wihout casting a into a
const A. And yet it is possible by using a delegate, which I think performs a
hidden cast operation.
 
Calling a const member function is allowed without casting from either thread-local mutable data or immutable data. Calling a non-const member function on a const object is what's illegal. That's what your original sample did. Constructing such a delegate (without casts) should be illegal and is a bug in dmd. Once a const-correct delegate is created, it should be usable like any other delegate.
I totally agree that the problem is the conversion of a [ void delegate() const] to a [void delegate()]. I totally agree that we should be allowed to call the const-correct delegate once it has been created. ie, this should be correct: void delegate() g = cast ( void delegate() ) (f); //here a cast would be necessary g(); //This would be OK, of course Concerning the bugzilla, the problem is that adding the 'const' and 'share' properties of the frame pointer as a part of the delegate's type is more like a new feature than a compiler bug, isn't it ? On the other hand, it looks like we all agree to say this is a must have feature. Do wee ?
Dec 28 2009
prev sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Mon, 28 Dec 2009 03:02:23 +0300, TM <ithink therefore.iam> wrote:

 Hello,
 I was dealing with delegates when I found that the const correctness of  
 the this pointer was 'lost' when accessing a member function of a class  
 through a temporary delegate.

 Here is an example:

 class A{
     void f () {}
 }

 const A a = new A;
 a.f() //error, this is normal.
 auto g = &a.f;
 g(); //no error. Is this normal ?

 I may be wrong, but it seems to me that as the const property of the  
 this pointer is linked to the instance of the class (here 'a'), the  
 delegate 'g' should contain a const pointer to A (because 'a' is const).

 More generally, I seems that there is no management of the const  
 correctness of the frame pointer of a delegate in D2

 What about adding the const keyword for delegates, as we do for methods  
 ? The const property of the delegate's frame pointer would be part of  
 its type.

 Example:

 void f()
 {
     int x;
     void g() const {
         x ++; //would produce an error since the type of g is [void  
 delegate() const]
    }
 }

 Any thoughts about this ?
It shouldn't compile: it makes no sense to can't non-const method through const reference, and the same applies to shared. OTOH, the following should just work: class A { void constMethod() const {} void sharedMethod() shared {} } const A a1 = new A(); void delegate() const dg1 = &a1.constMethod; shared A a2 = new shared A(); void delegate() shared dg2 = &a2.sharedMethod(); (in examples above, const and shared attributes are applied to both function pointer and frame pointer) I already wrote about a necessity of introduction of "shared delegates" in a separate thread. Until then, Thread creation is broken in D.
Dec 28 2009
parent reply =?UTF-8?B?U8O2bmtlIEx1ZHdpZw==?= writes:
Am 28.12.2009 18:44, schrieb Denis Koroskin:
 On Mon, 28 Dec 2009 03:02:23 +0300, TM <ithink therefore.iam> wrote:

 Hello,
 I was dealing with delegates when I found that the const correctness
 of the this pointer was 'lost' when accessing a member function of a
 class through a temporary delegate.

 Here is an example:

 class A{
 void f () {}
 }

 const A a = new A;
 a.f() //error, this is normal.
 auto g = &a.f;
 g(); //no error. Is this normal ?

 I may be wrong, but it seems to me that as the const property of the
 this pointer is linked to the instance of the class (here 'a'), the
 delegate 'g' should contain a const pointer to A (because 'a' is const).

 More generally, I seems that there is no management of the const
 correctness of the frame pointer of a delegate in D2

 What about adding the const keyword for delegates, as we do for
 methods ? The const property of the delegate's frame pointer would be
 part of its type.

 Example:

 void f()
 {
 int x;
 void g() const {
 x ++; //would produce an error since the type of g is [void delegate()
 const]
 }
 }

 Any thoughts about this ?
It shouldn't compile: it makes no sense to can't non-const method through const reference, and the same applies to shared. OTOH, the following should just work: class A { void constMethod() const {} void sharedMethod() shared {} } const A a1 = new A(); void delegate() const dg1 = &a1.constMethod; shared A a2 = new shared A(); void delegate() shared dg2 = &a2.sharedMethod(); (in examples above, const and shared attributes are applied to both function pointer and frame pointer) I already wrote about a necessity of introduction of "shared delegates" in a separate thread. Until then, Thread creation is broken in D.
Actually in both cases the error is not happening when the delegate is called but at the point where the delegate is created. _Creating_ a delegate to a non-const function should simply be impossible when a const object is bound (result in a compile time error). Similarily, for a shared object, the necessary synchronization code should be added to the delegate instead of having the shared attribute attached to the delegate. Typing the delegate will just create superfluous type combinations and complicate delegate usage - additionally it would be strange in the sense that it makes assumptions about the type of the "context pointer" without knowing what it is (object, stack frame, struct, ...). Sönke
Dec 28 2009
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 28 Dec 2009 15:18:21 -0500, Sönke Ludwig  
<ludwig_nospam informatik.uni-luebeck.de> wrote:


 Actually in both cases the error is not happening when the delegate is  
 called but at the point where the delegate is created. _Creating_ a  
 delegate to a non-const function should simply be impossible when a  
 const object is bound (result in a compile time error). Similarily, for  
 a shared object, the necessary synchronization code should be added to  
 the delegate instead of having the shared attribute attached to the  
 delegate.

 Typing the delegate will just create superfluous type combinations and  
 complicate delegate usage - additionally it would be strange in the  
 sense that it makes assumptions about the type of the "context pointer"  
 without knowing what it is (object, stack frame, struct, ...).
I'd agree that the typechecking for const (and immutable) can be done on the point of delegate creation, especially since one of the perks of delegates is you don't have to tell the compiler what type the context pointer is already. However, shared is a different story, since the calling convention is different. I think we need shared delegates as Denis said. They would be different types than normal delegates. What you are proposing is that every shared function that you wish to take a delegate of has a "wrapper" function also implemented which does the shared synchronization and then calls the underlying function, just so it looks like a normal delegate. I'd rather see the compiler simply treat shared delegates as different types than normal delegates, and insert the synchronization code before calling the delegate. With such delegates, you could easily make a shared delegate wrapper type that did exactly what you wanted anyways. It appears that delegate contravariance isn't required here after all (unfortunately), but we definitely need shared delegates. -Steve
Dec 28 2009
parent =?UTF-8?B?U8O2bmtlIEx1ZHdpZw==?= writes:
Am 28.12.2009 21:46, schrieb Steven Schveighoffer:
 On Mon, 28 Dec 2009 15:18:21 -0500, Sönke Ludwig
 <ludwig_nospam informatik.uni-luebeck.de> wrote:


 Actually in both cases the error is not happening when the delegate is
 called but at the point where the delegate is created. _Creating_ a
 delegate to a non-const function should simply be impossible when a
 const object is bound (result in a compile time error). Similarily,
 for a shared object, the necessary synchronization code should be
 added to the delegate instead of having the shared attribute attached
 to the delegate.

 Typing the delegate will just create superfluous type combinations and
 complicate delegate usage - additionally it would be strange in the
 sense that it makes assumptions about the type of the "context
 pointer" without knowing what it is (object, stack frame, struct, ...).
I'd agree that the typechecking for const (and immutable) can be done on the point of delegate creation, especially since one of the perks of delegates is you don't have to tell the compiler what type the context pointer is already. However, shared is a different story, since the calling convention is different. I think we need shared delegates as Denis said. They would be different types than normal delegates. What you are proposing is that every shared function that you wish to take a delegate of has a "wrapper" function also implemented which does the shared synchronization and then calls the underlying function, just so it looks like a normal delegate. I'd rather see the compiler simply treat shared delegates as different types than normal delegates, and insert the synchronization code before calling the delegate. With such delegates, you could easily make a shared delegate wrapper type that did exactly what you wanted anyways. It appears that delegate contravariance isn't required here after all (unfortunately), but we definitely need shared delegates. -Steve
I agree on what you say about shared. My thought was just that maybe there is actually no place where you would ever declare a function which gets a shared delegate, because normally it does not matter for the caller on what kind of thing a delegate is called. Seen that way, an implicit non-shared wrapper would be a helpful feature for the 99% case. Of course, having shared delegates would never hurt (maybe for some class internal use?). Sönke
Jan 02 2010