www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Error: cannot implicitly convert expression (this) of type const(S)

reply Jonathan M Davis <jmdavisProg gmx.com> writes:
Okay, if I try and compile the following program.

struct S
{
     property S save() const
    {
        return this;
    }

    int[] _val;
}

void main()
{
}


I get the error message

d.d(5): Error: cannot implicitly convert expression (this) of type const(S)=
 to S


If I remove const from save(), then it works, but with const there, it does=
n't.=20
If I change _val to int or a struct type which does not contain an array, i=
t=20
works. If I change _val to a struct type which has a member variable which =
is an=20
array, it doesn't work.

=46rom the looks of it, there's something about having an array as a member=
=20
variable which makes this blow up. Perhaps it has to do with the fact that =
_val=20
is a reference type and would be shared? Do I need to declare a postplit=20
constructor to fix this? Declaring one doesn't seem to help, but maybe I ju=
st=20
don't know how to declare them correctly. I tried

this(this)
{
    _val =3D _val.dup;
}


and it didn't do any good. Setting _val to null didn't work either.

Does anyone have any clues as to what I'm doing wrong? Or is this yet anoth=
er=20
bug in dmd?

=2D Jonathan M Davis
Sep 18 2010
next sibling parent Ivo Kasiuk <i.kasiuk gmx.de> writes:
Am Samstag, den 18.09.2010, 02:15 -0700 schrieb Jonathan M Davis:
 Okay, if I try and compile the following program.
=20
 struct S
 {
      property S save() const
     {
         return this;
     }
=20
     int[] _val;
 }
=20
 void main()
 {
 }
=20

Actually, wouldn't it be much more simple to just copy the struct instance directly, without a save() method: S s1; S s2 =3D s1; And you could add a postblit to control the copy behavior concerning _val. Or if save() is supposed to behave different than normal copying maybe something like this could be the solution: S save() const { return S(_val.dup); }
Sep 18 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sat, 18 Sep 2010 05:15:38 -0400, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 Okay, if I try and compile the following program.

 struct S
 {
      property S save() const
     {
         return this;
     }

     int[] _val;
 }

 void main()
 {
 }


 I get the error message

 d.d(5): Error: cannot implicitly convert expression (this) of type  
 const(S) to S

Yes, because you are converting "this" from a const(S) to an S to return it. Try: property const(S) save() const { return this; }
 If I remove const from save(), then it works, but with const there, it  
 doesn't.
 If I change _val to int or a struct type which does not contain an  
 array, it
 works. If I change _val to a struct type which has a member variable  
 which is an
 array, it doesn't work.

Because arrays are reference types. You can implicitly convert a const(int) to an int because it's not a reference type. But you can't implicitly convert a const(int *) to an int * because it's a reference type. That axiom of implicit conversion is propagated to structs as well -- if a struct has only value types, then it can be implicitly converted, reference types cannot. I'll give you an example of why your version should not work. Let's say it *did* compile, then this function is allowed to violate const: void foo(const(S) s) { S s2 = s.save; s2._val[0]++; assert(s2._val[0] == s._val[0]); // oops, I modified s._val[0], but s is const! }
 From the looks of it, there's something about having an array as a member
 variable which makes this blow up. Perhaps it has to do with the fact  
 that _val
 is a reference type and would be shared? Do I need to declare a postplit
 constructor to fix this? Declaring one doesn't seem to help, but maybe I  
 just
 don't know how to declare them correctly.

In reality, you cannot make save const, unless you want to do a deep copy (but I recommend against that, save should be a quick operation). The solution is actually inout. With inout, you can assert that save does not modify the data, but that it doesn't alter the constancy of the actual type. property inout(S) save() inout { return this; } Should work for S, const(S) and immutable(S). However, inout doesn't work at all right now. Please vote for bug http://d.puremagic.com/issues/show_bug.cgi?id=3748 (oh wait, you already did, but others should too :) -Steve
Sep 18 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday 18 September 2010 09:58:15 Steven Schveighoffer wrote:
 On Sat, 18 Sep 2010 05:15:38 -0400, Jonathan M Davis <jmdavisProg gmx.com>
 
 wrote:
 Okay, if I try and compile the following program.
 
 struct S
 {
 
      property S save() const
     {
     
         return this;
     
     }
     
     int[] _val;
 
 }
 
 void main()
 {
 }
 
 
 I get the error message
 
 d.d(5): Error: cannot implicitly convert expression (this) of type
 const(S) to S

Yes, because you are converting "this" from a const(S) to an S to return it. Try: property const(S) save() const { return this; }
 If I remove const from save(), then it works, but with const there, it
 doesn't.
 If I change _val to int or a struct type which does not contain an
 array, it
 works. If I change _val to a struct type which has a member variable
 which is an
 array, it doesn't work.

Because arrays are reference types. You can implicitly convert a const(int) to an int because it's not a reference type. But you can't implicitly convert a const(int *) to an int * because it's a reference type. That axiom of implicit conversion is propagated to structs as well -- if a struct has only value types, then it can be implicitly converted, reference types cannot. I'll give you an example of why your version should not work. Let's say it *did* compile, then this function is allowed to violate const: void foo(const(S) s) { S s2 = s.save; s2._val[0]++; assert(s2._val[0] == s._val[0]); // oops, I modified s._val[0], but s is const! }
 From the looks of it, there's something about having an array as a member
 variable which makes this blow up. Perhaps it has to do with the fact
 that _val
 is a reference type and would be shared? Do I need to declare a postplit
 constructor to fix this? Declaring one doesn't seem to help, but maybe I
 just
 don't know how to declare them correctly.

In reality, you cannot make save const, unless you want to do a deep copy (but I recommend against that, save should be a quick operation).

Well, I was trying to make a deep copy (that's what it does if it doesn't have an array in it anyway). I suppose whether it really needs to be const or not depends on what you're trying to do with it. I'm definitely trying to create a range here, and if save() is supposed to make a copy of the range, whether that needs to be a shallow copy or a deep copy depends on what the std.algorithm stuff does with it. When I think copy, I usually think deep copy, but that's not necessarily the case. I will have to enquire as to the intent of save(). For value types, shallow copy and deep copy are the same, so it's not an issue. But for class ranges or struct ranges with references, it does become an issue.
 
 The solution is actually inout.  With inout, you can assert that save does
 not modify the data, but that it doesn't alter the constancy of the actual
 type.
 
  property inout(S) save() inout
 {
    return this;
 }
 
 Should work for S, const(S) and immutable(S).
 
 However, inout doesn't work at all right now.  Please vote for bug
 http://d.puremagic.com/issues/show_bug.cgi?id=3748 (oh wait, you already
 did, but others should too :)
 
 -Steve

I would _love_ for all of the const-related issues to be fixed, and I most definitely voted on at least the major ones. Heck, when I started using D back with 2.017 or so, I chose D2 over D1 because D2 had const and D1 didn't.
Sep 18 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday 18 September 2010 06:45:51 Ivo Kasiuk wrote:
 Am Samstag, den 18.09.2010, 02:15 -0700 schrieb Jonathan M Davis:
 Okay, if I try and compile the following program.
 
 struct S
 {
 
      property S save() const
     {
     
         return this;
     
     }
     
     int[] _val;
 
 }
 
 void main()
 {
 }

Actually, wouldn't it be much more simple to just copy the struct instance directly, without a save() method:

No. The whole point here is to have a save() method. It's for a range, and forward ranges have the save() method because a range doesn't necessarily have to be a struct and just doing a straight up copy with a class doesn't work.
 
 S s1;
 S s2 = s1;
 
 And you could add a postblit to control the copy behavior concerning
 _val.
 
 Or if save() is supposed to behave different than normal copying maybe
 something like this could be the solution:
 
 S save() const {
   return S(_val.dup);
 }

For some reason, I haven't gotten postblit to work. Outright creating a new one that's the same rather than trying to use the automatic copy mechanisms does seem like a good idea though. Thanks. - Jonathan M Davis
Sep 18 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sat, 18 Sep 2010 17:20:31 -0400, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 On Saturday 18 September 2010 09:58:15 Steven Schveighoffer wrote:

 In reality, you cannot make save const, unless you want to do a deep  
 copy
 (but I recommend against that, save should be a quick operation).

Well, I was trying to make a deep copy (that's what it does if it doesn't have an array in it anyway). I suppose whether it really needs to be const or not depends on what you're trying to do with it. I'm definitely trying to create a range here, and if save() is supposed to make a copy of the range, whether that needs to be a shallow copy or a deep copy depends on what the std.algorithm stuff does with it. When I think copy, I usually think deep copy, but that's not necessarily the case. I will have to enquire as to the intent of save(). For value types, shallow copy and deep copy are the same, so it's not an issue. But for class ranges or struct ranges with references, it does become an issue.

You don't want a deep copy of a range. All you want to copy is the iteration state, not the data. save is definitely supposed to be shallow. I.e. you should copy the range itself, not what the range points to. -Steve
Sep 20 2010
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, September 20, 2010 04:11:05 Steven Schveighoffer wrote:
 You don't want a deep copy of a range.  All you want to copy is the
 iteration state, not the data.
 
 save is definitely supposed to be shallow.  I.e. you should copy the range
 itself, not what the range points to.

That makes sense, but whenever I see copy, I pretty much always think deep copy, though obviously a copy isn't always a deep copy or you wouldn't need the word deep. - Jonathan M Davis
Sep 20 2010