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

Reply via email to