Thread overview
Accessing mutable data that isn't
Nov 20, 2013
Spott
Nov 21, 2013
Jesse Phillips
Nov 21, 2013
qznc
Nov 21, 2013
Jonathan M Davis
Nov 21, 2013
bearophile
Nov 25, 2013
Spott
Nov 21, 2013
Jonathan M Davis
Nov 25, 2013
Spott
Nov 26, 2013
Jonathan M Davis
November 20, 2013
I've been screwing around with templates lately, and I'm
attempting to figure out why the following won't compile:

struct value
{
     int a;

     const auto
         opBinary(string op, T)(in T rhs) const pure {
             static if (op == "+")
                 return intermediateValue!(value.plus,this,rhs)();
         }

     ref value opAssign(T)( in T t ) {
         a = t.a;
         return this;
     }

     static
     int plus(T1, T2)(in T1 x, in T2 y) pure {
         return x.a + y.a;
     }

}

struct intermediateValue(alias Op, alias A, alias B)
{

     auto opBinary(string op, T)(in T rhs) const pure {
         static if (op == "+")
             return intermediateValue!(value.plus,this,rhs)();
     }

     @property auto a() const pure {
         return Op(A, B);
     }

}

void main()
{
     value a = value(2);
     value b = value(3);
     value c;
     c = a + b;
}

The error is:
d_playground.d(34): Error: pure nested function 'a' cannot access
mutable data 'this'
d_playground.d(34): Error: pure nested function 'a' cannot access
mutable data 'this'
d_playground.d(10): Error: template instance
d_playground.value.opBinary!("+",
value).opBinary.intermediateValue!(plus, this, rhs) error
instantiating
d_playground.d(44):        instantiated from here: opBinary!("+",
value)
d_playground.d(44): Error: template instance
d_playground.value.opBinary!("+", value) error instantiating

What is going on?  Why is 'a' not allowed to "access" mutable
data (even though it isn't modifying it)? How do I tell the
compiler to pass "this" in a const fashion?
November 21, 2013
On Wednesday, 20 November 2013 at 22:49:42 UTC, Spott wrote:
> I've been screwing around with templates lately, and I'm
> attempting to figure out why the following won't compile:
>
> struct value
> {
>      int a;
>
>      const auto
>          opBinary(string op, T)(in T rhs) const pure {
>              static if (op == "+")
>                  return intermediateValue!(value.plus,this,rhs)();
>          }

const here is redundant, probably wanted const(auto) which isn't valid syntax. The function being const may already be returning a const type.

> What is going on?  Why is 'a' not allowed to "access" mutable
> data (even though it isn't modifying it)? How do I tell the
> compiler to pass "this" in a const fashion?

I'm not seeing an issue with the declarations. The function being declared as const is what make 'this' const. Probably should file as a bug if you don't get any confirmation soon. And reply with the bug entry.
November 21, 2013
On Wednesday, 20 November 2013 at 22:49:42 UTC, Spott wrote:
> I've been screwing around with templates lately, and I'm
> attempting to figure out why the following won't compile:
>
> struct value
> {
>      int a;
>
>      const auto
>          opBinary(string op, T)(in T rhs) const pure {
>              static if (op == "+")
>                  return intermediateValue!(value.plus,this,rhs)();
>          }
>
>      ref value opAssign(T)( in T t ) {
>          a = t.a;
>          return this;
>      }
>
>      static
>      int plus(T1, T2)(in T1 x, in T2 y) pure {
>          return x.a + y.a;
>      }
>
> }
>
> struct intermediateValue(alias Op, alias A, alias B)
> {
>
>      auto opBinary(string op, T)(in T rhs) const pure {
>          static if (op == "+")
>              return intermediateValue!(value.plus,this,rhs)();
>      }
>
>      @property auto a() const pure {
>          return Op(A, B);
>      }
>
> }
>
> void main()
> {
>      value a = value(2);
>      value b = value(3);
>      value c;
>      c = a + b;
> }
>
> The error is:
> d_playground.d(34): Error: pure nested function 'a' cannot access
> mutable data 'this'
> d_playground.d(34): Error: pure nested function 'a' cannot access
> mutable data 'this'
> d_playground.d(10): Error: template instance
> d_playground.value.opBinary!("+",
> value).opBinary.intermediateValue!(plus, this, rhs) error
> instantiating
> d_playground.d(44):        instantiated from here: opBinary!("+",
> value)
> d_playground.d(44): Error: template instance
> d_playground.value.opBinary!("+", value) error instantiating
>
> What is going on?  Why is 'a' not allowed to "access" mutable
> data (even though it isn't modifying it)? How do I tell the
> compiler to pass "this" in a const fashion?

No answer, but two notes.

First, use dpaste for such code snippets: http://dpaste.dzfl.pl/f2f39b32

Second, what are you trying to do? intermediateValue is a struct without members. I am not sure what 'this' means in such a case.
November 21, 2013
On Wednesday, November 20, 2013 23:49:42 Spott wrote:
> I've been screwing around with templates lately, and I'm attempting to figure out why the following won't compile:
> 
> struct value
> {
>       int a;
> 
>       const auto
>           opBinary(string op, T)(in T rhs) const pure {
>               static if (op == "+")
>                   return intermediateValue!(value.plus,this,rhs)();
>           }
> 
>       ref value opAssign(T)( in T t ) {
>           a = t.a;
>           return this;
>       }
> 
>       static
>       int plus(T1, T2)(in T1 x, in T2 y) pure {
>           return x.a + y.a;
>       }
> 
> }
> 
> struct intermediateValue(alias Op, alias A, alias B)
> {
> 
>       auto opBinary(string op, T)(in T rhs) const pure {
>           static if (op == "+")
>               return intermediateValue!(value.plus,this,rhs)();
>       }
> 
>       @property auto a() const pure {
>           return Op(A, B);
>       }
> 
> }
> 
> void main()
> {
>       value a = value(2);
>       value b = value(3);
>       value c;
>       c = a + b;
> }
> 
> The error is:
> d_playground.d(34): Error: pure nested function 'a' cannot access
> mutable data 'this'
> d_playground.d(34): Error: pure nested function 'a' cannot access
> mutable data 'this'
> d_playground.d(10): Error: template instance
> d_playground.value.opBinary!("+",
> value).opBinary.intermediateValue!(plus, this, rhs) error
> instantiating
> d_playground.d(44):        instantiated from here: opBinary!("+",
> value)
> d_playground.d(44): Error: template instance
> d_playground.value.opBinary!("+", value) error instantiating
> 
> What is going on?  Why is 'a' not allowed to "access" mutable data (even though it isn't modifying it)? How do I tell the compiler to pass "this" in a const fashion?


pure functions can only access their arguments and global/static constants. a's only argument is its invisible this pointer. Op, A, and B are aliases to stuff outside of a. I suppose that an argument could be made that because the're template arguments to the type that a is a part of that they should be considered to be arguments to a like the this pointer is, but you are essentially trying to have it access data which is not one of its arguments and that violates purity.

But all in all, I find your code quite bizarre and difficult to understand - particularly your use of aliases - so it's kind of hard for me to say how valid it is. I'm surprised that you can get away with feeding a purely runtime argument to a template as an alias (namely rhs). I wouldn't have thought that that would be valid. In most cases, all template alias parameters get used for is passing in predicates to functions (which are almost invariably delegates or lambdas). So, clearly my understanding of how alias template parameters work is too limited.

- Jonathan M Davis
November 21, 2013
On Thursday, November 21, 2013 07:48:34 qznc wrote:
> First, use dpaste for such code snippets: http://dpaste.dzfl.pl/f2f39b32

Really? I find it annoying when people do that unless the code is quite long. It's much easier to have it just be in the message IMHO. Also, it has the benefit of not having to worry about the link not being valid in the future, potentially rendering the message impossible to understand. I have no idea how long dpaste links stick around; a while I expect, but there's no guarantee that they'll be around as long as the forum or its archives will be.

- Jonathan M Davis
November 21, 2013
Jonathan M Davis:

> I find it annoying when people do that unless the code is quite long.
> It's much easier to have it just be in the message IMHO. Also, it has the
> benefit of not having to worry about the link not being valid in the future,

I agree with Jonathan. External sites should be avoided unless the code is very long. (And you can even attach files in the D newsgroups when the code is a little longer). On Bugzilla code you have to always avoid code external sites (unless it's GitHub, etc).

Bye,
bearophile
November 25, 2013
On Thursday, 21 November 2013 at 06:48:40 UTC, qznc wrote:
> On Wednesday, 20 November 2013 at 22:49:42 UTC, Spott wrote:
>> I've been screwing around with templates lately, and I'm
>> attempting to figure out why the following won't compile:
>>
>> struct value
>> {
>>     int a;
>>
>>     const auto
>>         opBinary(string op, T)(in T rhs) const pure {
>>             static if (op == "+")
>>                 return intermediateValue!(value.plus,this,rhs)();
>>         }
>>
>>     ref value opAssign(T)( in T t ) {
>>         a = t.a;
>>         return this;
>>     }
>>
>>     static
>>     int plus(T1, T2)(in T1 x, in T2 y) pure {
>>         return x.a + y.a;
>>     }
>>
>> }
>>
>> struct intermediateValue(alias Op, alias A, alias B)
>> {
>>
>>     auto opBinary(string op, T)(in T rhs) const pure {
>>         static if (op == "+")
>>             return intermediateValue!(value.plus,this,rhs)();
>>     }
>>
>>     @property auto a() const pure {
>>         return Op(A, B);
>>     }
>>
>> }
>>
>> void main()
>> {
>>     value a = value(2);
>>     value b = value(3);
>>     value c;
>>     c = a + b;
>> }
>>
>> The error is:
>> d_playground.d(34): Error: pure nested function 'a' cannot access
>> mutable data 'this'
>> d_playground.d(34): Error: pure nested function 'a' cannot access
>> mutable data 'this'
>> d_playground.d(10): Error: template instance
>> d_playground.value.opBinary!("+",
>> value).opBinary.intermediateValue!(plus, this, rhs) error
>> instantiating
>> d_playground.d(44):        instantiated from here: opBinary!("+",
>> value)
>> d_playground.d(44): Error: template instance
>> d_playground.value.opBinary!("+", value) error instantiating
>>
>> What is going on?  Why is 'a' not allowed to "access" mutable
>> data (even though it isn't modifying it)? How do I tell the
>> compiler to pass "this" in a const fashion?
>
> No answer, but two notes.
>
> First, use dpaste for such code snippets: http://dpaste.dzfl.pl/f2f39b32
>
> Second, what are you trying to do? intermediateValue is a struct without members. I am not sure what 'this' means in such a case.

intermediateValue is a structure that ideally should only exist
at compile time.

Theoretically, (at least in my head), opAssign will traverse the
expression and reduce it to the addition that it is inside
opAssign.

I'm attempting to create a vector DSL (similar to blaze-lib:
https://code.google.com/p/blaze-lib/), but starting with just a
plain number.  Mostly it is for fun, it is a way of working on my
understanding of the compile time primitives and template system.
November 25, 2013
On Thursday, 21 November 2013 at 07:23:09 UTC, Jonathan M Davis
wrote:
> On Wednesday, November 20, 2013 23:49:42 Spott wrote:
>> I've been screwing around with templates lately, and I'm
>> attempting to figure out why the following won't compile:
>> 
>> struct value
>> {
>>       int a;
>> 
>>       const auto
>>           opBinary(string op, T)(in T rhs) const pure {
>>               static if (op == "+")
>>                   return intermediateValue!(value.plus,this,rhs)();
>>           }
>> 
>>       ref value opAssign(T)( in T t ) {
>>           a = t.a;
>>           return this;
>>       }
>> 
>>       static
>>       int plus(T1, T2)(in T1 x, in T2 y) pure {
>>           return x.a + y.a;
>>       }
>> 
>> }
>> 
>> struct intermediateValue(alias Op, alias A, alias B)
>> {
>> 
>>       auto opBinary(string op, T)(in T rhs) const pure {
>>           static if (op == "+")
>>               return intermediateValue!(value.plus,this,rhs)();
>>       }
>> 
>>       @property auto a() const pure {
>>           return Op(A, B);
>>       }
>> 
>> }
>> 
>> void main()
>> {
>>       value a = value(2);
>>       value b = value(3);
>>       value c;
>>       c = a + b;
>> }
>> 
>> The error is:
>> d_playground.d(34): Error: pure nested function 'a' cannot access
>> mutable data 'this'
>> d_playground.d(34): Error: pure nested function 'a' cannot access
>> mutable data 'this'
>> d_playground.d(10): Error: template instance
>> d_playground.value.opBinary!("+",
>> value).opBinary.intermediateValue!(plus, this, rhs) error
>> instantiating
>> d_playground.d(44):        instantiated from here: opBinary!("+",
>> value)
>> d_playground.d(44): Error: template instance
>> d_playground.value.opBinary!("+", value) error instantiating
>> 
>> What is going on?  Why is 'a' not allowed to "access" mutable
>> data (even though it isn't modifying it)? How do I tell the
>> compiler to pass "this" in a const fashion?
>
>
> pure functions can only access their arguments and global/static constants.
> a's only argument is its invisible this pointer. Op, A, and B are aliases to
> stuff outside of a. I suppose that an argument could be made that because
> the're template arguments to the type that a is a part of that they should be
> considered to be arguments to a like the this pointer is, but you are
> essentially trying to have it access data which is not one of its arguments
> and that violates purity.
>
> But all in all, I find your code quite bizarre and difficult to understand -
> particularly your use of aliases - so it's kind of hard for me to say how
> valid it is. I'm surprised that you can get away with feeding a purely runtime
> argument to a template as an alias (namely rhs). I wouldn't have thought that
> that would be valid. In most cases, all template alias parameters get used for
> is passing in predicates to functions (which are almost invariably delegates
> or lambdas). So, clearly my understanding of how alias template parameters
> work is too limited.
>
> - Jonathan M Davis

Why is rhs a purely runtime argument?  I would think it would be
known at compile time.
November 26, 2013
On Monday, November 25, 2013 18:34:30 Spott wrote:
> Why is rhs a purely runtime argument? I would think it would be known at compile time.

Function arguments are runtime entities, not compile-time entities and therefore cannot be used in places where a compile-time entity is required. e.g. this is illegal

auto foo(int i)(int j)
{
 return i * j;
}

auto bar(int k)
{
 return foo!k(5);
}

because k is not known at compile time. Yes, it's true that if a function is used during CTFE, then its function arguments would technically be known at compile time, as the compiler is in the middle of compiling your program, however, from the function's perspective, it's runtime. It just so happens that it's being run at compile time. e.g.

auto foo(int i)
{
 return i;
}

enum f = foo(5);

f must be known at compile time, so foo is called and run at compile time, but from foo's perspective, it's being run, not compiled. So, it can only do the things that it could do at runtime (plus whatever additional restrictions CTFE imposes - e.g. no I/O).

Template parameters are compile-time entities and thus must be known at compile time. However, aliases are a bit funny in that they alias the symbol rather than using its value, so apparently, under some set of circumstances, a template alias parameter can accept a runtime argument, because it's the symbol that gets used and not its value, meaning that its value is not calculated until runtime, so it works. But any normal template parameter's value must be known at compile time.

- Jonathan M Davis