Skip to content

Commit 36f7800

Browse files
authored
Merge pull request #2281 from RazvanN7/patch-9
Update struct.dd - postblit documentation
2 parents c69379b + 0ab9071 commit 36f7800

File tree

1 file changed

+265
-0
lines changed

1 file changed

+265
-0
lines changed

spec/struct.dd

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,271 @@ $(GNAME Postblit):
566566
}
567567
---
568568

569+
$(P Depending on the struct layout, the compiler may generate the following
570+
internal postblit functions:)
571+
572+
$(OL
573+
$(LI `void __postblit()`. The compiler assigns this name to the explicitly
574+
defined postblit `this(this)` so that it can be treated exactly as
575+
a normal function. Note that if a struct defines a postblit, it cannot
576+
define a function named `__postblit` - no matter the signature -
577+
as this would result in a compilation error due to the name conflict.)
578+
$(LI `void __fieldPostblit()`. If a struct `X` has at least one `struct`
579+
member that in turn defines (explicitly or implicitly) a postblit, then a field
580+
postblit is generated for `X` that calls all the underlying postblits
581+
of the struct fields in declaration order.)
582+
$(LI `void __aggrPostblit()`. If a struct has an explicitly defined postblit
583+
and at least 1 struct member that has a postblit (explicit or implicit)
584+
an aggregated postblit is generated which calls `__fieldPostblit` first
585+
and then `__postblit`.)
586+
$(LI `void __xpostblit()`. The field and aggregated postblits, although
587+
generated for a struct, are not actual struct members. In order to be able
588+
to call them, the compiler internally creates an alias, called `__xpostblit`
589+
which is a member of the struct and which points to the generated postblit that
590+
is the most inclusive.)
591+
)
592+
593+
$(SPEC_RUNNABLE_EXAMPLE_COMPILE
594+
---
595+
// struct with alias __xpostblit = __postblit
596+
struct X
597+
{
598+
this(this) {}
599+
}
600+
601+
// struct with alias __xpostblit = __fieldPostblit
602+
// which contains a call to X.__xpostblit
603+
struct Y
604+
{
605+
X a;
606+
}
607+
608+
// struct with alias __xpostblit = __aggrPostblit which contains
609+
// a call to Y.__xpostblit and a call to Z.__postblit
610+
struct Z
611+
{
612+
Y a;
613+
this(this) {}
614+
}
615+
616+
void main()
617+
{
618+
// X has __postblit and __xpostblit (pointing to __postblit)
619+
static assert(__traits(hasMember, X, "__postblit"));
620+
static assert(__traits(hasMember, X, "__xpostblit"));
621+
622+
// Y does not have __postblit, but has __xpostblit (pointing to __fieldPostblit)
623+
static assert(!__traits(hasMember, Y, "__postblit"));
624+
static assert(__traits(hasMember, Y, "__xpostblit"));
625+
// __fieldPostblit is not a member of the struct
626+
static assert(!__traits(hasMember, Y, "__fieldPostblit"));
627+
628+
// Z has __postblit and __xpostblit (pointing to __aggrPostblit)
629+
static assert(__traits(hasMember, Z, "__postblit"));
630+
static assert(__traits(hasMember, Z, "__xpostblit"));
631+
// __aggrPostblit is not a member of the struct
632+
static assert(!__traits(hasMember, Z, "__aggrPostblit"));
633+
}
634+
---
635+
)
636+
637+
$(P Neither of the above postblits is defined for structs that don't
638+
define `this(this)` and don't have fields that transitively define it.
639+
If a struct does not define a postblit (implicit or explicit) but
640+
defines functions that use the same name/signature as the internally
641+
generated postblits, the compiler is able to identify that the functions
642+
are not actual postblits and does not insert calls to them when the
643+
struct is copied. Example:)
644+
645+
$(SPEC_RUNNABLE_EXAMPLE_COMPILE
646+
---
647+
struct X
648+
{}
649+
650+
int a;
651+
652+
struct Y
653+
{
654+
int a;
655+
X b;
656+
void __fieldPostPostblit()
657+
{
658+
a = 42;
659+
}
660+
}
661+
662+
void main()
663+
{
664+
static assert(!__traits(hasMember, X, "__postblit"));
665+
static assert(!__traits(hasMember, X, "__xpostblit"));
666+
667+
static assert(!__traits(hasMember, Y, "__postblit"));
668+
static assert(!__traits(hasMember, Y, "__xpostblit"));
669+
670+
Y y;
671+
auto y2 = y;
672+
assert(a == 0); // __fieldPostBlit does not get called
673+
}
674+
---
675+
)
676+
677+
$(P Postblits cannot be overloaded. If two or more postblits are defined,
678+
even if the signatures differ, the compiler assigns the
679+
`__postblit` name to both and later issues a conflicting function
680+
name error:)
681+
682+
---
683+
struct X
684+
{
685+
this(this) {}
686+
this(this) const {} // error: function X.__postblit conflicts with function X.__postblit
687+
}
688+
---
689+
690+
$(P The following describes the behavior of the
691+
qualified postblit definitions:)
692+
693+
$(OL
694+
$(LI `const`. When a postblit is qualified with `const` as in
695+
$(D this(this) const;) or $(D const this(this);) then the postblit
696+
is succesfully called on mutable (unqualified), `const`,
697+
and `immutable` objects, but the postblit cannot modify the object
698+
because it regards it as `const`; hence `const` postblits are of
699+
limited usefulness. Example:)
700+
701+
$(SPEC_RUNNABLE_EXAMPLE_COMPILE
702+
---
703+
struct S
704+
{
705+
int n;
706+
this(this) const
707+
{
708+
import std.stdio : writeln;
709+
writeln("postblit called");
710+
//++n; // error: cannot modify this.n in `const` function
711+
}
712+
}
713+
714+
void main()
715+
{
716+
S s1;
717+
auto s2 = s1;
718+
const S s3;
719+
auto s4 = s3;
720+
immutable S s5;
721+
auto s6 = s5;
722+
}
723+
---
724+
)
725+
$(LI `immutable`. When a postblit is qualified with `immutable`
726+
as in $(D this(this) immutable) or $(D immutable this(this))
727+
the code is ill-formed. The `immutable` postblit passes the
728+
compilation phase but cannot be invoked. Example:)
729+
730+
---
731+
struct Y
732+
{
733+
// not invoked anywhere, no error is issued
734+
this(this) immutable
735+
{ }
736+
}
737+
738+
struct S
739+
{
740+
this(this) immutable
741+
{ }
742+
}
743+
744+
void main()
745+
{
746+
S s1;
747+
auto s2 = s1; // error: immutable method `__postblit` is not callable using a mutable object
748+
const S s3;
749+
auto s4 = s3; // error: immutable method `__postblit` is not callable using a mutable object
750+
immutable S s5;
751+
auto s6 = s5; // error: immutable method `__postblit` is not callable using a mutable object
752+
}
753+
---
754+
755+
$(LI `shared`. When a postblit is qualified with `shared` as in
756+
$(D this(this) shared) or $(D shared this(this)) solely `shared`
757+
objects may invoke the postblit; attempts of postbliting unshared
758+
objects will result in compile time errors:)
759+
760+
---
761+
struct S
762+
{
763+
this(this) shared
764+
{ }
765+
}
766+
767+
void main()
768+
{
769+
S s1;
770+
auto s2 = s1; // error: shared method `__postblit` is not callable using a non-shared object
771+
const S s3;
772+
auto s4 = s3; // error: shared method `__postblit` is not callable using a non-shared object
773+
immutable S s5;
774+
auto s6 = s5; // error: shared method `__postblit` is not callable using a non-shared object
775+
776+
// calling the shared postblit on a shared object is accepted
777+
shared S s7;
778+
auto s8 = s7;
779+
}
780+
---
781+
)
782+
783+
$(P An unqualified postblit will get called even if the
784+
struct is instantiated as `immutable` or `const`, but
785+
the compiler issues an error if the struct is instantiated
786+
as shared:)
787+
788+
$(SPEC_RUNNABLE_EXAMPLE_COMPILE
789+
---
790+
struct S
791+
{
792+
int n;
793+
this(this) { ++n; }
794+
}
795+
796+
void main()
797+
{
798+
immutable S a; // shared S a; => error : non-shared method is not callable using a shared object
799+
auto a2 = a;
800+
import std.stdio: writeln;
801+
writeln(a2.n); // prints 1
802+
}
803+
---
804+
)
805+
806+
$(P From a postblit perspective, qualifiying the struct definition
807+
yields the same result as explicitly qualifying the postblit.)
808+
809+
$(P The following table lists all the possibilities of grouping
810+
qualifiers for a postblit associated with the type of object that
811+
needs to be used in order to succesfully invoke the postblit:)
812+
813+
$(TABLE_10 $(ARGS Qualifier Groups),
814+
$(VERTROW object type to be invoked on, $(D const), $(D immutable), $(D shared)),
815+
$(TROW any object type, $(YES), $(NO), $(NO) )
816+
$(TROW uncallable, $(NO), $(YES), $(NO) )
817+
$(TROW shared object, $(NO), $(NO), $(YES))
818+
$(TROW uncallable, $(YES), $(YES), $(NO) )
819+
$(TROW shared object, $(YES), $(NO), $(YES))
820+
$(TROW uncallable, $(NO), $(YES), $(YES))
821+
$(TROW uncallable, $(YES), $(YES), $(YES))
822+
)
823+
824+
$(P Note that when `const` and `immutable` are used to explicitly
825+
qualify a postblit as in `this(this) const immutable;` or
826+
`const immutable this(this);` - the order in which the qualifiers
827+
are declared does not matter - the compiler generates a `conflicting
828+
attribute error`, however declaring the struct as `const`/`immutable`
829+
and the postblit as `immutable`/`const` achieves the effect of applying
830+
both qualifiers to the postblit. In both cases the postblit is
831+
qualified with the more restrictive qualifier, which is `immutable`.
832+
)
833+
569834
$(P Unions may not have fields that have postblits.)
570835

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

0 commit comments

Comments
 (0)