Thread overview
Member function passed through template alias only requiring `this` in certain conditions?
Jul 19, 2018
Emma
Jul 19, 2018
Ali Çehreli
Jul 20, 2018
Timoses
July 19, 2018
Hello,

I’ve got this piece of code:

---
import std.stdio;

void test(alias fn1, alias fn2)()
{
    fn1();
    fn2();
}

struct Foo
{
    void foo()
    {
        test!(bar, baz);
    }

    void bar()
    {}

    void baz()
    {}
}
---

If I try to compile it, dmd complains, which I guess makes sense:

---
Error: need this for bar of type void()
Error: need this for baz of type void()
---

However, if I change line 13 from `test!(bar, baz);` to `test!(() => bar, baz);`, it compiles and executes fine, calling both the `bar` and `baz` member functions.

Why is this? Shouldn’t it complain about `baz` needing `this`? Does the lambda in the first template argument somehow “pull in” the `this` reference for `baz`, too?

Thanks!
July 19, 2018
On 07/19/2018 08:12 AM, Emma wrote:

> void test(alias fn1, alias fn2)()
> {
>      fn1();
>      fn2();
> }
>
> struct Foo
> {
>      void foo()
>      {
>          test!(bar, baz);
>      }
>
>      void bar()
>      {}
>
>      void baz()
>      {}
> }
> ---
>
> If I try to compile it, dmd complains, which I guess makes sense:
>
> ---
> Error: need this for bar of type void()
> Error: need this for baz of type void()
> ---
>
> However, if I change line 13 from `test!(bar, baz);` to `test!(() =>
> bar, baz);`, it compiles and executes fine, calling both the `bar` and
> `baz` member functions.
>
> Why is this? Shouldn’t it complain about `baz` needing `this`? Does the
> lambda in the first template argument somehow “pull in” the `this`
> reference for `baz`, too?
>
> Thanks!

I think it's a compiler bug. The second template argument should be a lambda as well. When I added the following lines to test()

    pragma(msg, typeof(fn1));
    pragma(msg, typeof(fn2));

the output is different:

void delegate() @system
void()

I think it's a bug probably related to multiple local lambdas. We had similar issues in the past. Although it works as is, I recommend you make the second one a lambda as well.

Others, please confirm that it's a bug and let's create a bug report.

Ali

July 20, 2018
On Thursday, 19 July 2018 at 22:16:22 UTC, Ali Çehreli wrote:
> On 07/19/2018 08:12 AM, Emma wrote:
> > [...]
> > If I try to compile it, dmd complains, which I guess makes
> sense:
> >
> > ---
> > Error: need this for bar of type void()
> > Error: need this for baz of type void()
> > ---
> >
> > [...]
>
> I think it's a compiler bug. The second template argument should be a lambda as well. When I added the following lines to test()
>
>     pragma(msg, typeof(fn1));
>     pragma(msg, typeof(fn2));
>
> the output is different:
>
> void delegate() @system
> void()

That is for the version of calling test with one lamdba and one function? (`test!(() => bar, baz)`)

Just a question: The compiler should not automatically convert those (functions) to lambdas?

Interesting is this:

void test(alias fn1, alias fn2)()
{
    pragma(msg, typeof(fn1)); // void delegate() @system
    pragma(msg, typeof(fn2)); // void()
    fn1();
    fn2(); // Error: this for baz needs to be type Boo not type Foo
}
struct Foo
{
    void foo()
    {
        test!(()=>bar, b.baz);
    }
    int i = 1337;

    struct Boo
    {
     	int j = 3;
        void baz()
    	{writeln(j);}
    }
    Boo b;

    void bar()
    {writeln(i);}
}
unittest
{
 	auto foo = Foo();
    foo.foo();
}

Looks like the compiler just assumes the context of the first delegate to be applicable for the second function?

>
> I think it's a bug probably related to multiple local lambdas. We had similar issues in the past. Although it works as is, I recommend you make the second one a lambda as well.
>
> Others, please confirm that it's a bug and let's create a bug report.
>
> Ali