March 12, 2018
On Monday, 12 March 2018 at 09:54:20 UTC, Simen Kjærås wrote:
>
> But I don't have a hook to update the pointer when the struct is moved. The GC may move my struct without informing me in any way. In fact, even just a copy construction will break this:
>
> struct S {
>     S* ptr;
>     this(int dummy) {
>         ptr = &this;
>     }
>     ~this() {
>         // This assert will fail.
>         assert(ptr == &this);
>     }
> }
>
> unittest {
>     S s1 = S(0);
>     S s2 = S(0);
>
>     assert(&s1 == s1.ptr);
>     assert(&s2 == s2.ptr);
>
>     s1 = s2;
> }
>
> The reason is copy construction makes a copy[1] of the lhs, then performs the copy construction[2]. If the copy construction[2] succeeds, the copy[1] is destroyed. If not, the copy[1] is blitted back over the original, to give the impression that nothing ever happened.
>
> When the destructor is called on the copy[1], &this returns a different address from what it did before, since it's a copy and logically resides at a different address.
>

Sure, you have.
https://dlang.org/spec/struct.html#assign-overload
Point #4.
In this case,

ref S opAssign(ref S rhs)
{
    return this;
}

Another point is, that I hope, that pointers don't move anywhere, as in C, by definition.

>
>> struct SomeType(alias fn) {}
>>
>> is (or has to be) lowered to something like
>>
>> struct SomeType
>> {
>>   typeof(fn)* fn;
>> }
>>
>> Even if fn contains a frame pointer to S it is perfectly legal to have such a type. SomeType would contain a delegate then.
>
> Indeed. But stack frames aren't copied or moved the way structs are.
>

Yes. This is how the structs are meant to be, I thought :)

>
>> However, I think, the syntax
>>
>>>> struct S {
>>>>     int n, m;
>>>>     SomeType!(() => n + m) a;
>>>> }
>>
>> is still invalid and
>>
>>>> struct S {
>>>>     int n, m;
>>>>     auto a() { return SomeType!(() => n + m)(); }
>>>> }
>>
>> has another semantics.
>>
>> The latter closures above the current values inside of S.
>> The former has to be constructible without the context, as it is perfectly legal to write
>>
>> struct Outer
>> {
>> 	struct Inner{}
>> }
>>
>> void main()
>> {
>>   auto i = Outer.Inner();
>> }
>>
>> but would be impossible with this syntax.
>
> I'm not using any inner structs in my examples. SomeType is assumed to be a separate type that knows nothing about S. If you're saying this should work:
>

Ah... I was unclear, I think...

> unittest {
>     auto tmp = typeof(S.a)();
> }

I thought, this shouldn't be possible (at least in my mind)

>
> It wouldn't, and such code is not possible in D today:
>
> struct S {
>     int n;
>     auto a() { return SomeType!(() => n)(); }
> }
>
> struct SomeType(alias fn) {
>     int get() { return fn(); }
> }
>
But this is clearly valid.

> unittest {
>    // cannot access frame pointer of foo.S.a.SomeType!(delegate () => this.n).SomeType
>    auto tmp = typeof(S.a())();
> }
>
For sure, tmp cannot be defined without an instance of S. So the correct unittest in my eyes would be:
unittest {
   S s;
   auto res = s.a;
   assert(res.get == S.init.n);
}


> --
>   Simen

March 12, 2018
On Monday, 12 March 2018 at 10:37:00 UTC, Alex wrote:
> Sure, you have.
> https://dlang.org/spec/struct.html#assign-overload
> Point #4.
> In this case,
>
> ref S opAssign(ref S rhs)
> {
>     return this;
> }

True. Can you fix these, too?

struct S {
    S* ptr;
    this(int dummy) {
        ptr = &this;
    }
    ~this() {
        assert(ptr == &this);
    }
}

void test(S s) {}

unittest {
    test(S(0));
}

S test2() {
    return S(0);
}

unittest {
    S s = test2();
}


> Another point is, that I hope, that pointers don't move anywhere, as in C, by definition.

And why not? D allows for moving garbage collectors.



>> unittest {
>>     auto tmp = typeof(S.a)();
>> }
>
> I thought, this shouldn't be possible (at least in my mind)
>
>> It wouldn't, and such code is not possible in D today:
>>
>> struct S {
>>     int n;
>>     auto a() { return SomeType!(() => n)(); }
>> }
>>
>> struct SomeType(alias fn) {
>>     int get() { return fn(); }
>> }
>>
> But this is clearly valid.

Yes, it's an example of code that works in D today, with similar semantics to those implied in the other example. It's meant to show that just like `auto tmp = typeof(S.a())()` doesn't work today, it shouldn't work with the types used in my other example.



>> unittest {
>>    // cannot access frame pointer of foo.S.a.SomeType!(delegate () => this.n).SomeType
>>    auto tmp = typeof(S.a())();
>> }
>>
> For sure, tmp cannot be defined without an instance of S. So the correct unittest in my eyes would be:
> unittest {
>    S s;
>    auto res = s.a;
>    assert(res.get == S.init.n);
> }

I think we may be speaking past one another. Yes, your unittest would be expected to pass.

If we go back to the example code:

struct S1 {
    int n, m;
    SomeType!(() => n + m) a;
}

vs.

struct S2 {
    int n, m;
    auto a() { return SomeType!(() => n + m)(); }
}

I would expect typeof(S1.a) and typeof(S2.a()) to be equivalent. I don't see a good reason why I should be able to construct an instance of typeof(S1.a), since I can't construct a typeof(S2.a()).

I see your point that "The latter closures above the current values inside of [S2]". I'm saying I want the former to do the same. I understand that it currently doesn't, and I argue that having it do the same would be a much more useful behavior. Apart from the fact it's impossible, I don't see any good reason not to make it work. :p

--
  Simen
March 12, 2018
On Monday, 12 March 2018 at 13:04:54 UTC, Simen Kjærås wrote:
> On Monday, 12 March 2018 at 10:37:00 UTC, Alex wrote:
>> Sure, you have.
>> https://dlang.org/spec/struct.html#assign-overload
>> Point #4.
>> In this case,
>>
>> ref S opAssign(ref S rhs)
>> {
>>     return this;
>> }
>
> True. Can you fix these, too?
>
> struct S {
>     S* ptr;
>     this(int dummy) {
>         ptr = &this;
>     }
>     ~this() {
>         assert(ptr == &this);
>     }
> }
>
> void test(S s) {}
>
> unittest {
>     test(S(0));
> }
>
> S test2() {
>     return S(0);
> }
>
> unittest {
>     S s = test2();
> }
>

ok... this is a little bit more tricky, as there is no assignment now. :)
But in this two cases I assume, you want to have explicit pass by reference, no?

struct S {

    S* ptr;
    this(int dummy) {
        ptr = &this;
    }
	
    ~this() {
        assert(ptr == &this);
    }
}

void test(ref S s){}

unittest {
    auto s = S(0);
    test(s);
    /*
    if I call test(S(0)) with void test(S s) as in your example,
    neither the postblit nor opAssign is called... hm... no idea why...
    */
}

auto test2() {
    return new S(0);
}

unittest {
    auto s = test2();
    /*
    This example differs more from the post before :) test2 is a true factory now,
    it has to grant the right setting of all members...
    */
}

>> Another point is, that I hope, that pointers don't move anywhere, as in C, by definition.
>
> And why not? D allows for moving garbage collectors.

If it were allowed, then "contiguous memory allocation" for arrays would be senseless.

>
>>> unittest {
>>>     auto tmp = typeof(S.a)();
>>> }
>>
>> I thought, this shouldn't be possible (at least in my mind)
>>
>>> It wouldn't, and such code is not possible in D today:
>>>
>>> struct S {
>>>     int n;
>>>     auto a() { return SomeType!(() => n)(); }
>>> }
>>>
>>> struct SomeType(alias fn) {
>>>     int get() { return fn(); }
>>> }
>>>
>> But this is clearly valid.
>
> Yes, it's an example of code that works in D today, with similar semantics to those implied in the other example. It's meant to show that just like `auto tmp = typeof(S.a())()` doesn't work today, it shouldn't work with the types used in my other example.
>
>
>
>>> unittest {
>>>    // cannot access frame pointer of foo.S.a.SomeType!(delegate () => this.n).SomeType
>>>    auto tmp = typeof(S.a())();
>>> }
>>>
>> For sure, tmp cannot be defined without an instance of S. So the correct unittest in my eyes would be:
>> unittest {
>>    S s;
>>    auto res = s.a;
>>    assert(res.get == S.init.n);
>> }
>
> I think we may be speaking past one another. Yes, your unittest would be expected to pass.
>
> If we go back to the example code:
>
> struct S1 {
>     int n, m;
>     SomeType!(() => n + m) a;
> }
>
> vs.
>
> struct S2 {
>     int n, m;
>     auto a() { return SomeType!(() => n + m)(); }
> }
>
> I would expect typeof(S1.a) and typeof(S2.a()) to be equivalent. I don't see a good reason why I should be able to construct an instance of typeof(S1.a), since I can't construct a typeof(S2.a()).
>
> I see your point that "The latter closures above the current values inside of [S2]". I'm saying I want the former to do the same. I understand that it currently doesn't, and I argue that having it do the same would be a much more useful behavior. Apart from the fact it's impossible, I don't see any good reason not to make it work. :p

I see your point too :)
But the latter form just grants the existence of an instance of S, whereas the first form doesn't.
By the way, this would work:

struct S1 {
    static int n, m; // added a static here.
    SomeType!(() => n + m) a;
}

struct SomeType(alias fn){}

> --
>   Simen
1 2
Next ›   Last »