View mode: basic / threaded / horizontal-split · Log in · Help
November 24, 2012
constructor instead of opCall for an instance of a template alias
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
Re: constructor instead of opCall for an instance of a template alias
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
Re: constructor instead of opCall for an instance of a template alias
> 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
Re: constructor instead of opCall for an instance of a template alias
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
Re: constructor instead of opCall for an instance of a template alias
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
Re: constructor instead of opCall for an instance of a template alias
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
Re: constructor instead of opCall for an instance of a template alias
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
Top | Discussion index | About this forum | D home