Thread overview
Implicit conversion of struct with methods to immutable in pure function fails
Jul 16, 2018
Timoses
Jul 16, 2018
Simen Kjærås
Jul 16, 2018
Timoses
Jul 16, 2018
Seb
Jul 17, 2018
Simen Kjærås
Jul 18, 2018
Timoses
Jul 19, 2018
Simen Kjærås
Jul 19, 2018
Timoses
July 16, 2018
Why does this fail?

    struct F
    {
        int i;
        ushort _x;
        void x(ushort v) pure
        {_x = v;}
        ushort x() const
        { return _x; }
    }

    immutable F f1 = () pure {
    F lf = F();
    return lf; }();
    // Error: cannot implicitly convert expression delegate () => lf() of type F to immutable(F)

    F makeF() pure
    {
     	F lf = F();
        return lf;
    }
    immutable F f2 = makeF();
    // Error: cannot implicitly convert expression makeF() of type F to immutable(F)

Removing the methods in struct F compiles fine.

Background: I have a mixin(bitfields!(...)) in the struct which utilizes member functions.


/////////////
Idea:
Just found out that it works when making the struct static. But why does that help?

Is it because the compiler wouldn't be able to check whether methods in the struct are accessing non-immutable data in the enclosing context?
That would not make much sense though because the following works:

// Compiles fine!
int modi = 3;
void main ()
{
    static struct F
    {
        int var() immutable { return modi; } // accessing modi from module scope
    }
    immutable f = () pure { F f = F(); return f; }();
}

So my explanation wouldn't make sense, since why would it be okay to use module-scope data and not enclosing context data?

So where does that limitation come from that I implicitly convert a nested struct to immutable in a pure function?


July 16, 2018
On Monday, 16 July 2018 at 11:43:03 UTC, Timoses wrote:
> Why does this fail?

It doesn't. Not using DMD 2.081.1 under Windows, at least. I tried adding a bitfield since you mentioned it, but it compiles nicely for me. Which version of DMD are you using, and are you having the issues with the exact code you posted here?

--
  Simen
July 16, 2018
On Monday, 16 July 2018 at 12:00:57 UTC, Simen Kjærås wrote:
> On Monday, 16 July 2018 at 11:43:03 UTC, Timoses wrote:
>> Why does this fail?
>
> It doesn't. Not using DMD 2.081.1 under Windows, at least. I tried adding a bitfield since you mentioned it, but it compiles nicely for me. Which version of DMD are you using, and are you having the issues with the exact code you posted here?
>
> --
>   Simen

https://run.dlang.io/is/Pgs527

I'm on 2.080.1. But above is on latest 2.081.1 I believe.

Note that the bottom code snippet in the original post does work, while the first one does not.
July 16, 2018
On Monday, 16 July 2018 at 13:13:53 UTC, Timoses wrote:
> On Monday, 16 July 2018 at 12:00:57 UTC, Simen Kjærås wrote:
>> On Monday, 16 July 2018 at 11:43:03 UTC, Timoses wrote:
>>> Why does this fail?
>>
>> It doesn't. Not using DMD 2.081.1 under Windows, at least. I tried adding a bitfield since you mentioned it, but it compiles nicely for me. Which version of DMD are you using, and are you having the issues with the exact code you posted here?
>>
>> --
>>   Simen
>
> https://run.dlang.io/is/Pgs527
>
> I'm on 2.080.1. But above is on latest 2.081.1 I believe.
>
> Note that the bottom code snippet in the original post does work, while the first one does not.

Yep, run.dlang.io automatically updates itself to the latest compiler (you can check this e.g. with -v).
July 17, 2018
On Monday, 16 July 2018 at 13:13:53 UTC, Timoses wrote:
> On Monday, 16 July 2018 at 12:00:57 UTC, Simen Kjærås wrote:
>> On Monday, 16 July 2018 at 11:43:03 UTC, Timoses wrote:
>>> Why does this fail?
>>
>> It doesn't. Not using DMD 2.081.1 under Windows, at least. I tried adding a bitfield since you mentioned it, but it compiles nicely for me. Which version of DMD are you using, and are you having the issues with the exact code you posted here?
>>
>> --
>>   Simen
>
> https://run.dlang.io/is/Pgs527
>
> I'm on 2.080.1. But above is on latest 2.081.1 I believe.
>
> Note that the bottom code snippet in the original post does work, while the first one does not.

That makes sense. The problem is F has a context pointer to the main() block, since it's a non-static struct with methods inside a block. It doesn't actually use the context pointer for anything, so it possibly shouldn't have one, but it does, so we have to work around it.

The fix is to mark F as static, or move it outside the main() block.

--
  Simen
July 18, 2018
On Tuesday, 17 July 2018 at 06:24:12 UTC, Simen Kjærås wrote:
>
> That makes sense. The problem is F has a context pointer to the main() block, since it's a non-static struct with methods inside a block. It doesn't actually use the context pointer for anything, so it possibly shouldn't have one, but it does, so we have to work around it.
>
> The fix is to mark F as static, or move it outside the main() block.
>
> --
>   Simen

Thanks for the explanation.

But why is a context pointer a problem? Is it problematic because the context pointer to the main scope can not guarantee `immutable`? E.g. if I happened to use data from main in a function of the immutable struct then... well then what?
The struct would still be immutable, but what would prevent a function from using non-immutable data?
E.g. I could do this

    int gnumber = 3;

    struct F
    {
        int i;
        void fun() immutable
        {
            gnumber = 5;
        }
    }

    void main ()
    {
        immutable F f = F();
        f.fun;
    }

I declared `fun()` to be an immutable function. So calling the immutable struct function `F.fun()` changes a module scope int. The same could be applied to local data of the main scope, however the following fails:

    void main ()
    {
        int mnumber = 3;
        struct F
        {
            int i;
            void fun() immutable
            {
                // Error: immutable function onlineapp.main.F.fun cannot access mutable data mnumber
                mnumber = 5;
            }
        }

        immutable F f = F();
        f.fun;
    }

Is this connected why I can't implicitly convert a local struct with a context pointer to immutable? What's the reason behind it?
July 19, 2018
On Wednesday, 18 July 2018 at 11:28:54 UTC, Timoses wrote:
> But why is a context pointer a problem? Is it problematic because the context pointer to the main scope can not guarantee `immutable`? E.g. if I happened to use data from main in a function of the immutable struct then... well then what?
> The struct would still be immutable, but what would prevent a function from using non-immutable data?

It's a known bug: https://issues.dlang.org/show_bug.cgi?id=18563
In the associated discussion (https://forum.dlang.org/thread/p7lp2b$1jod$1@digitalmars.com), Steven Schveighoffer points out that an immutable struct may be passed to other threads, which would give one thread access to another thread's stack. This could be a good enough reason to prevent this kind of conversion, but a better error message would still make sense.

--
  Simen
July 19, 2018
On Thursday, 19 July 2018 at 06:35:36 UTC, Simen Kjærås wrote:
> On Wednesday, 18 July 2018 at 11:28:54 UTC, Timoses wrote:
>> But why is a context pointer a problem? Is it problematic because the context pointer to the main scope can not guarantee `immutable`? E.g. if I happened to use data from main in a function of the immutable struct then... well then what?
>> The struct would still be immutable, but what would prevent a function from using non-immutable data?
>
> It's a known bug: https://issues.dlang.org/show_bug.cgi?id=18563
> In the associated discussion (https://forum.dlang.org/thread/p7lp2b$1jod$1@digitalmars.com), Steven Schveighoffer points out that an immutable struct may be passed to other threads, which would give one thread access to another thread's stack. This could be a good enough reason to prevent this kind of conversion, but a better error message would still make sense.
>
> --
>   Simen

Thanks so much for the pointer, Simen. Interesting discussion.