Skip to content

Commit aace249

Browse files
committed
Explain behavior of member=expr inside constructors
1 parent c1d57d6 commit aace249

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed

spec/struct.dd

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,96 @@ $(SECTION3 $(LEGACY_LNAME2 Struct-Constructor, struct-constructor, Struct Constr
348348
}
349349
------
350350

351+
$(P Inside a constructor, the first occurrence (in lexical order) of assignments
352+
of the form $(CODE member = expression;) are handled differently than usual
353+
assignments. The first such assignment in lexical order is converted to a
354+
constructor call for the member's type. Example:)
355+
356+
------
357+
import std.stdio;
358+
359+
struct A
360+
{
361+
this(int x) { writef("A.this(%s)", x); }
362+
}
363+
364+
struct B
365+
{
366+
A a;
367+
this(int x)
368+
{
369+
write("[= ");
370+
a = x;
371+
writeln(" =]");
372+
// a = x; does not compile here, it already occurred lexically.
373+
}
374+
}
375+
376+
void main(string[] args)
377+
{
378+
auto b = B(10);
379+
// b.a = 10; does not compile here, A does not define opAssign(int).
380+
}
381+
------
382+
383+
$(P The program above prints the line $(CODE "[= A.this(10) =]"). Anywhere else
384+
attempting to assign an integer to an object of type `A` would count as an
385+
assignment (and is not compilable because `A` does not define `opAssign(int)`).)
386+
387+
$(P Finding the first assignment in lexical order is flow-sensitive upon the
388+
`if` statement. Consider a change to struct `B` in the previous example as
389+
follows:)
390+
391+
------
392+
struct B
393+
{
394+
A a;
395+
this(int x)
396+
{
397+
if (x < 0)
398+
a = -x;
399+
else
400+
a = x;
401+
}
402+
}
403+
------
404+
405+
$(P This code issues a constructor call on each branch of the `if` statement.
406+
However, such flow sensitivity is limited. There is no static or dynamic
407+
analysis of coverage of the `if` statement. For example:)
408+
409+
------
410+
struct B
411+
{
412+
A a;
413+
this(int x)
414+
{
415+
if (false) a = 0; // constructor call even if never covered
416+
a = x; // error, cannot assign
417+
}
418+
}
419+
------
420+
421+
$(P Also, member assignments inside loops are never considered constructors,
422+
even if it can be determined statically that the loop executes at most once.
423+
Example:)
424+
425+
------
426+
struct B
427+
{
428+
A a;
429+
this(int x)
430+
{
431+
foreach (i; 0 .. x ? 0 : 1) a = i; // error, cannot assign
432+
}
433+
}
434+
------
435+
436+
$(P If an exception is thrown at any point from within a constructor,
437+
destructors are called for all members, in reverse lexical order of their
438+
declaration. Members that have not been explicitly initialized in the
439+
constructor will have their `.init` values upon destruction.)
440+
351441
$(P A constructor qualifier allows the object to be constructed with
352442
that specific qualifier.
353443
)

0 commit comments

Comments
 (0)