Thread overview
constructor instead of opCall for an instance of a template alias
Nov 24, 2012
comco
Nov 25, 2012
Rob T
Nov 25, 2012
comco
Nov 25, 2012
Ali Çehreli
Nov 25, 2012
comco
Nov 25, 2012
Maxim Fomin
Nov 25, 2012
Rob T
November 24, 2012
I have the following snippet:

struct A(alias Method)
{
    string s;
    this(Method method) {
        method(s); // 5
    }
}

struct B
{
    this(int i) { }
    void opCall(string s) { }
}


void main() {
    A!B(B(0));
}


This code fails to compile with the following errors:
test.d(5): Error: constructor test.B.this (int i) is not callable using argument types (string)
test.d(5): Error: cannot implicitly convert expression (this.s) of type string to int
test.d(17): Error: template instance test.A!(B) error instantiating
shell returned 1

It looks like the compiler is confusing the `method` instance parameter with the `Method` class.
If I replace line 5 with `method.opCall(s);` it compiles.

Can you explain this behaviour please?
November 25, 2012
On Saturday, 24 November 2012 at 20:34:39 UTC, comco wrote:
> I have the following snippet:
>
> struct A(alias Method)
> {
>     string s;
>     this(Method method) {
>         method(s); // 5
>     }
> }
>
> struct B
> {
>     this(int i) { }
>     void opCall(string s) { }
> }
>
>
> void main() {
>     A!B(B(0));
> }
>
>
> This code fails to compile with the following errors:
> test.d(5): Error: constructor test.B.this (int i) is not callable using argument types (string)
> test.d(5): Error: cannot implicitly convert expression (this.s) of type string to int
> test.d(17): Error: template instance test.A!(B) error instantiating
> shell returned 1
>
> It looks like the compiler is confusing the `method` instance parameter with the `Method` class.
> If I replace line 5 with `method.opCall(s);` it compiles.
>
> Can you explain this behaviour please?


struct A(alias Method) is a template taking in some to be defined type named Method. I'm not sure why "alias" is there, you can remove it.

A!B(...) defines a struct A with B typed in as Method, so call to method(s); is the same as B(string) but there's no B.this(string) defined for B, causing the compiler to complain.

method.opCall(s); is B.opCall(s) which is defined, and that is why it will compile.

--rt


November 25, 2012
> A!B(...) defines a struct A with B typed in as Method, so call to method(s); is the same as B(string)
Why is that? Here method is an instance of the type Method, not the type Method itself, so by saying `method(s)` we can't mean a constructor. Something very strange happens...
I have a simpler example now:

import std.stdio;

enum ok = true;

struct A
{
    int i;

    static if (ok)
    {
        this(int i) {
            this.i = i;
            writeln("A.ctor();");
        }
    }

    void opCall(int i) {
        writeln("A.opCall()");
    }
}


void main() {
    static if (ok)
    {
        A(0);
    }
    else
    {
        A();
    }
}

This example works as expected, and it prints: A.ctor().
But if I change it to enum ok = false, then the above code doesn't compile with an Error: function LowestAncestor.A.opCall (int i) is not callable using argument types (). So this might mean that the compiler becomes confused somehow and thinks that we want to call opCall on a static instance (which is not even possible, because opCall is an instance method), and not the default constructor.
November 25, 2012
On 11/25/2012 07:27 AM, comco wrote:
>
>> A!B(...) defines a struct A with B typed in as Method, so call to
>> method(s); is the same as B(string)
> Why is that? Here method is an instance of the type Method, not the type
> Method itself,

That could be case but you original code did use a type name for Method:

    A!B(B(0));

This would be using an instance:

    B instance;
    A!instance(B(0));

It would not compile because you would be using an instance as a type name in the constructor signature:

    this(Method method) {    // <-- ERROR: Method is an instance
        // ...
    }

> so by saying `method(s)` we can't mean a constructor.
> Something very strange happens...

The situation with constructors and opCall() are still confused. There are a number of bugs open but not exactly this one.

> I have a simpler example now:

Even simpler:

struct A
{
    int i;

    void opCall(int i) {
    }
}

void main() {
    auto a = A(42);
}

Error: variable deneme.main.a type void is inferred from initializer opCall(42), and variables cannot be of type void
Error: expression opCall(42) is void and has no value

The bug is that a non-static opCall() is chosen instead of the default constructor. As you say, non-static opCall() overloads should not interfere with construction.

Could you please create a bug report:

  http://d.puremagic.com/issues/

Ali

November 25, 2012
On Sunday, 25 November 2012 at 15:27:53 UTC, comco wrote:
>
>> A!B(...) defines a struct A with B typed in as Method, so call to method(s); is the same as B(string)
> Why is that? Here method is an instance of the type Method, not the type Method itself, so by saying `method(s)` we can't mean a constructor. Something very strange happens...
> I have a simpler example now:

Welcome to D structs creation puzzle.

> skipped

If you change return type from void to e.x. int, dmd will still complain "Error: need 'this' to access member opCall".

There are two problems here: firstly, by some reason dmd does not distinguish between static and non static methods in a sense that it allows to call static method on instance and takes into account non-static methods with expression operating on types (like this case).

Secondly, and again by some reason three "semantic things" such as struct literals, struct constructors and opCalls use same syntax like "S()" (let alone it may be non-member function). This makes their priority order an issue - for 2.060 version it is: 1)opCall 2)ctor 3)literal. This is why dmd complains about "A.opCall (int i) is not callable using argument types ()": it recognizes an opCall method, does not take into account staticness and tries to convert A() into opCall invocation.

Recently I saw a major pull affecting this behavior, so in 2.061 the situation may be changed (I haven't bother to figure yet). In practice this makes a tricky thing to understand what S() is and creates a problem when you e.x. heavily use struct ctor, write opCall method and everything breaks due to dmd tries now to call opCall instead of ctor.

November 25, 2012
On Sunday, 25 November 2012 at 16:01:39 UTC, Ali Çehreli wrote:

> Could you please create a bug report:
>
>   http://d.puremagic.com/issues/
>
> Ali

Filed an issue 9078.
November 25, 2012
On Sunday, 25 November 2012 at 16:42:03 UTC, Maxim Fomin wrote:
> Recently I saw a major pull affecting this behavior, so in 2.061 the situation may be changed (I haven't bother to figure yet). In practice this makes a tricky thing to understand what S() is and creates a problem when you e.x. heavily use struct ctor, write opCall method and everything breaks due to dmd tries now to call opCall instead of ctor.

Looks like something was fixed with 2.061, the sample code now refuses to compile, which is correct behavior.

void main() {
    auto a = A(42);
}

source/main.d(10): Error: need 'this' for opCall type void(int i)

--rt