Thread overview
Error: cannot implicitly convert expression (this) of type const(S) to S
Sep 18, 2010
Jonathan M Davis
Sep 18, 2010
Ivo Kasiuk
Sep 18, 2010
Jonathan M Davis
Sep 18, 2010
Jonathan M Davis
Sep 20, 2010
Jonathan M Davis
September 18, 2010
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 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.

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. I tried

this(this)
{
    _val = _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 another bug in dmd?

- Jonathan M Davis
September 18, 2010
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:

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);
}

September 18, 2010
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
September 18, 2010
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.
September 18, 2010
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
September 20, 2010
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
September 20, 2010
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