Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
265 changes: 265 additions & 0 deletions spec/struct.dd
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,271 @@ $(GNAME Postblit):
}
---

$(P Depending on the struct layout, the compiler may generate the following
internal postblit functions:)

$(OL
$(LI `void __postblit()`. The compiler assigns this name to the explicitly
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please specify entire signatures (as I did in the commit I added) and make sure those I added are correct.

defined postblit `this(this)` so that it can be treated exactly as
a normal function. Note that if a struct defines a postblit, it cannot
define a function named `__postblit` - no matter the signature -
as this would result in a compilation error due to the name conflict.)
$(LI `void __fieldPostblit()`. If a struct `X` has at least one `struct`
member that in turn defines (explicitly or implicitly) a postblit, then a field
postblit is generated for `X` that calls all the underlying postblits
of the struct fields in declaration order.)
$(LI `void __aggrPostblit()`. If a struct has an explicitly defined postblit
and at least 1 struct member that has a postblit (explicit or implicit)
an aggregated postblit is generated which calls `__fieldPostblit` first
and then `__postblit`.)
$(LI `void __xpostblit()`. The field and aggregated postblits, although
generated for a struct, are not actual struct members. In order to be able
to call them, the compiler internally creates an alias, called `__xpostblit`
which is a member of the struct and which points to the generated postblit that
is the most inclusive.)
)

$(SPEC_RUNNABLE_EXAMPLE_COMPILE
---
// struct with alias __xpostblit = __postblit
struct X
{
this(this) {}
}

// struct with alias __xpostblit = __fieldPostblit
// which contains a call to X.__xpostblit
struct Y
{
X a;
}

// struct with alias __xpostblit = __aggrPostblit which contains
// a call to Y.__xpostblit and a call to Z.__postblit
struct Z
{
Y a;
this(this) {}
}

void main()
{
// X has __postblit and __xpostblit (pointing to __postblit)
static assert(__traits(hasMember, X, "__postblit"));
static assert(__traits(hasMember, X, "__xpostblit"));

// Y does not have __postblit, but has __xpostblit (pointing to __fieldPostblit)
static assert(!__traits(hasMember, Y, "__postblit"));
static assert(__traits(hasMember, Y, "__xpostblit"));
// __fieldPostblit is not a member of the struct
static assert(!__traits(hasMember, Y, "__fieldPostblit"));

// Z has __postblit and __xpostblit (pointing to __aggrPostblit)
static assert(__traits(hasMember, Z, "__postblit"));
static assert(__traits(hasMember, Z, "__xpostblit"));
// __aggrPostblit is not a member of the struct
static assert(!__traits(hasMember, Z, "__aggrPostblit"));
}
---
)

$(P Neither of the above postblits is defined for structs that don't
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should specify somewhere (perhaps here) what happens if someone does NOT define this(this) but DOES define the special functions. Is the compiler going to insert the calls? (Hopefully not!)

define `this(this)` and don't have fields that transitively define it.
If a struct does not define a postblit (implicit or explicit) but
defines functions that use the same name/signature as the internally
generated postblits, the compiler is able to identify that the functions
are not actual postblits and does not insert calls to them when the
struct is copied. Example:)

$(SPEC_RUNNABLE_EXAMPLE_COMPILE
---
struct X
{}

int a;

struct Y
{
int a;
X b;
void __fieldPostPostblit()
{
a = 42;
}
}

void main()
{
static assert(!__traits(hasMember, X, "__postblit"));
static assert(!__traits(hasMember, X, "__xpostblit"));

static assert(!__traits(hasMember, Y, "__postblit"));
static assert(!__traits(hasMember, Y, "__xpostblit"));

Y y;
auto y2 = y;
assert(a == 0); // __fieldPostBlit does not get called
}
---
)

$(P Postblits cannot be overloaded. If two or more postblits are defined,
even if the signatures differ, the compiler assigns the
`__postblit` name to both and later issues a conflicting function
name error:)

---
struct X
{
this(this) {}
this(this) const {} // error: function X.__postblit conflicts with function X.__postblit
}
---

$(P The following describes the behavior of the
qualified postblit definitions:)

$(OL
$(LI `const`. When a postblit is qualified with `const` as in
$(D this(this) const;) or $(D const this(this);) then the postblit
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the "is ill-formed" part here because the code is accepted. Please don't forget - we're documenting behavior here, not what should happen. If we deprecate this with the next version, then yes please do mention it's ill-formed but no diagnostic is issues by front-end versions prior to 2.xxx.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will deprecate this with the next version, but @wilzbach argued that information that gets outdated should not be in the spec. I thought that saying it's ill-formed but not mentioning the deprecation is the best compromise since you can actually call the function but it's not doing what it is supposed to.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's helpful to give that information so people who see invalid code getting compiled don't get confused.

is succesfully called on mutable (unqualified), `const`,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the backquotes around mutable because mutable is not a keyword.

and `immutable` objects, but the postblit cannot modify the object
because it regards it as `const`; hence `const` postblits are of
limited usefulness. Example:)

$(SPEC_RUNNABLE_EXAMPLE_COMPILE
---
struct S
{
int n;
this(this) const
{
import std.stdio : writeln;
writeln("postblit called");
//++n; // error: cannot modify this.n in `const` function
}
}

void main()
{
S s1;
auto s2 = s1;
const S s3;
auto s4 = s3;
immutable S s5;
auto s6 = s5;
}
---
)
$(LI `immutable`. When a postblit is qualified with `immutable`
as in $(D this(this) immutable) or $(D immutable this(this))
the code is ill-formed. The `immutable` postblit passes the
compilation phase but cannot be invoked. Example:)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version when this will be diagnosed? I think we can leave this text as is and add a mention once we merge the appropriate PR.


---
struct Y
{
// not invoked anywhere, no error is issued
this(this) immutable
{ }
}

struct S
{
this(this) immutable
{ }
}

void main()
{
S s1;
auto s2 = s1; // error: immutable method `__postblit` is not callable using a mutable object
const S s3;
auto s4 = s3; // error: immutable method `__postblit` is not callable using a mutable object
immutable S s5;
auto s6 = s5; // error: immutable method `__postblit` is not callable using a mutable object
}
---

$(LI `shared`. When a postblit is qualified with `shared` as in
$(D this(this) shared) or $(D shared this(this)) solely `shared`
objects may invoke the postblit; attempts of postbliting unshared
objects will result in compile time errors:)

---
struct S
{
this(this) shared
{ }
}

void main()
{
S s1;
auto s2 = s1; // error: shared method `__postblit` is not callable using a non-shared object
const S s3;
auto s4 = s3; // error: shared method `__postblit` is not callable using a non-shared object
immutable S s5;
auto s6 = s5; // error: shared method `__postblit` is not callable using a non-shared object

// calling the shared postblit on a shared object is accepted
shared S s7;
auto s8 = s7;
}
---
)

$(P An unqualified postblit will get called even if the
struct is instantiated as `immutable` or `const`, but
the compiler issues an error if the struct is instantiated
as shared:)

$(SPEC_RUNNABLE_EXAMPLE_COMPILE
---
struct S
{
int n;
this(this) { ++n; }
}

void main()
{
immutable S a; // shared S a; => error : non-shared method is not callable using a shared object
auto a2 = a;
import std.stdio: writeln;
writeln(a2.n); // prints 1
}
---
)

$(P From a postblit perspective, qualifiying the struct definition
yields the same result as explicitly qualifying the postblit.)

$(P The following table lists all the possibilities of grouping
qualifiers for a postblit associated with the type of object that
needs to be used in order to succesfully invoke the postblit:)

$(TABLE_10 $(ARGS Qualifier Groups),
$(VERTROW object type to be invoked on, $(D const), $(D immutable), $(D shared)),
$(TROW any object type, $(YES), $(NO), $(NO) )
$(TROW uncallable, $(NO), $(YES), $(NO) )
$(TROW shared object, $(NO), $(NO), $(YES))
$(TROW uncallable, $(YES), $(YES), $(NO) )
$(TROW shared object, $(YES), $(NO), $(YES))
$(TROW uncallable, $(NO), $(YES), $(YES))
$(TROW uncallable, $(YES), $(YES), $(YES))
)

$(P Note that when `const` and `immutable` are used to explicitly
qualify a postblit as in `this(this) const immutable;` or
`const immutable this(this);` - the order in which the qualifiers
are declared does not matter - the compiler generates a `conflicting
attribute error`, however declaring the struct as `const`/`immutable`
and the postblit as `immutable`/`const` achieves the effect of applying
both qualifiers to the postblit. In both cases the postblit is
qualified with the more restrictive qualifier, which is `immutable`.
)

$(P Unions may not have fields that have postblits.)

$(H2 $(LEGACY_LNAME2 StructDestructor, struct-destructor, Struct Destructors))
Expand Down