Thread overview
Strange Error using parameterized opAssign for a struct
Jul 04, 2011
d coder
Jul 04, 2011
Robert Jacques
Jul 05, 2011
d coder
Jul 05, 2011
d coder
Jul 05, 2011
d coder
Jul 05, 2011
d coder
July 04, 2011
Greetings All

I have a Struct (see minimized code below) which is failing for a simple assignment. I am using DMD 2.053, and am getting an error:

> implicit.d(14): Error: cannot implicitly convert expression (foo1) of type
Foo!(4) to Foo!(NN)

Am I doing something wrong, or have I hit a DMD bug? Kindly note that this happens only when I parameterize the struct using an Integral parameter, and works file when using string as parameter (as with struct Bar in the code).

Regards
- Puneet

// File implicit.d
struct Foo (size_t N) {
  void opAssign (size_t NN)(Foo!(NN) f) {/*do nothing*/}
}
struct Bar (string S) {
  void opAssign (string SS)(Bar!(SS) f) {/*do nothing*/}
}
void main() {
  Bar!"BAR1" bar1;
  Bar!"BAR2" bar2;
  bar2 = bar1; // this compiles fine
  Foo!4 foo1;
  Foo!4 foo2;
  foo2 = foo1; // compilation error
}


July 04, 2011
On Mon, 04 Jul 2011 05:58:45 -0400, d coder <dlang.coder@gmail.com> wrote:

> Greetings All
>
> I have a Struct (see minimized code below) which is failing for a simple assignment. I am using DMD 2.053, and am getting an error:
>
>> implicit.d(14): Error: cannot implicitly convert expression (foo1) of type Foo!(4) to Foo!(NN)
>
> Am I doing something wrong, or have I hit a DMD bug? Kindly note that this happens only when I parameterize the struct using an Integral parameter, and works file when using string as parameter (as with struct Bar in the code).
>
> Regards
> - Puneet

Yes and No. DMD has trouble deducing the correct template parameters for implicit function template instantiating when you make the template parameter the input to another template. The solution is to match a general type T and constrain it. (DMD can do these more complex matches inside an is expression, etc. So you can rewrite your code as:

// File implicit.d
struct Foo (size_t N) {
  void opAssign (T:Foo!NN,size_t NN)(T f) {/*do nothing*/}
}
struct Bar (string S) {
  void opAssign (string SS)(Bar!(SS) f) {/*do nothing*/}
}
void main() {
  Bar!"BAR1" bar1;
  Bar!"BAR2" bar2;
  bar2 = bar1;			// this compiles fine
  Foo!4 foo1;
  Foo!4 foo2;
  foo2 = foo1;			// Now compiles
}

Also, if you want to instantiate a Foo!NN, IIRC there's similar bug where the type becomes Foo!NN and not Foo!4. The solution is to use Foo!(NN+0) (or T) instead.
July 05, 2011
Thanks Robert

Meanwhile, I also found that my code works it I change parameter type to int instead of size_t. Looks like DMD fails because of some issue with implicit conversions (or lack of it) between integral types.

Anyway the solution you provided is more elegant and I will adopt that.

Regards
- Puneet


July 05, 2011
Greetings Robert/All

Robert suggested that I put my opAssign method as:

void opAssign (T:Foo!NN,size_t NN)(T f) { }

That works. But I want to find out if it is possible to write the opAssign template method with a conditional in the following form. This will help me optimize code better. Kindly suggest what would come in place of ....... below.

void opAssign (T)(T f) if(is(T .......)) { }

Regards
- Puneet


July 05, 2011
>
>
> Robert suggested that I put my opAssign method as:
>
> void opAssign (T:Foo!NN,size_t NN)(T f) { }
>
> That works. But I want to find out if it is possible to write the opAssign template method with a conditional in the following form. This will help me optimize code better. Kindly suggest what would come in place of ....... below.
>
> void opAssign (T)(T f) if(is(T .......)) { }
>


void opAssign (T)(T f) if(is(T L : Foo!(NN,MM), int NN, int MM)) { }

I found that the above declaration compiles. But again it compiles with int as parameter type. Fails for size_t.

Any other idea?

Regards
- Puneet


July 05, 2011
Hello Robert

As I try adding more code, I get into more and more issues with integral parameters. See the following code:

Regards
- Puneet

// File implicit.d
alias int R;
// alias size_t R;
struct Foo (R N) {
  version(v1) {void opAssign (R NN)(Foo!NN f) {/*do nothing*/}}
  version(v2) {void opAssign (T:Foo!NN, R NN)(T f) {/*do nothing*/}}
  @property Foo!(I) range(R I)() {return Foo!(I)();}
}
void main() {
  Foo!4 foo1;
  Foo!7 foo2;
  foo2 = foo1; // compiles with both v2 and v1/(only when R is aliased to
int)
  foo2 = foo1.range!4; // compiles with v1/int and v2/int -- does not
compile with R alias to size_t
  foo2 = foo1.range!2; // compiles only with v1/int -- does not compile with
v2 at all
}