Skip to content

Commit ce0ee3b

Browse files
authored
implement isOverlapped trait (dlang#22640)
1 parent b30656f commit ce0ee3b

6 files changed

Lines changed: 267 additions & 1 deletion

File tree

changelog/dmd.isOverlapped.dd

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
New trait `__traits(isOverlapped, field)` to detect overlapping fields
2+
3+
D now provides a compile-time trait to check whether a struct or class field overlaps with other fields in memory. This is useful for serialization libraries, code generators, and metaprogramming tasks that need to identify fields sharing the same memory location.
4+
5+
The trait takes a single field argument, returning `true` if the field's storage overlaps with other fields (typically because it is part of a union).
6+
7+
```d
8+
struct S
9+
{
10+
int a;
11+
union
12+
{
13+
int x; // overlaps with y
14+
float y; // overlaps with x
15+
}
16+
int b;
17+
}
18+
19+
static assert(__traits(isOverlapped, S.x)); // true
20+
static assert(__traits(isOverlapped, S.y)); // true
21+
static assert(!__traits(isOverlapped, S.a)); // false - regular field
22+
static assert(!__traits(isOverlapped, S.b)); // false - regular field
23+
```
24+
25+
The trait works with both anonymous and named unions:
26+
27+
```d
28+
union NamedUnion
29+
{
30+
int x;
31+
float y;
32+
}
33+
34+
static assert(__traits(isOverlapped, NamedUnion.x)); // true
35+
static assert(__traits(isOverlapped, NamedUnion.y)); // true
36+
```
37+
38+
This trait is particularly useful for:
39+
- Serialization libraries that need to handle only one field from overlapping sets
40+
- Understanding memory layout and field interaction
41+
- Implementing correct destructors for types with overlapping fields
42+
- Generic code that needs to reason about field storage semantics
43+
44+
The trait exposes DMD's internal overlap tracking (`VarDeclaration.overlapped`), providing a direct way to query this semantic property.

compiler/src/dmd/frontend.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8800,6 +8800,7 @@ struct Id final
88008800
static Identifier* isAbstractClass;
88018801
static Identifier* isArithmetic;
88028802
static Identifier* isAssociativeArray;
8803+
static Identifier* isOverlapped;
88038804
static Identifier* isBitfield;
88048805
static Identifier* isFinalClass;
88058806
static Identifier* isTemplate;

compiler/src/dmd/id.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ immutable Msgtable[] msgtable =
418418
{ "isAbstractClass" },
419419
{ "isArithmetic" },
420420
{ "isAssociativeArray" },
421+
{ "isOverlapped" },
421422
{ "isBitfield" },
422423
{ "isFinalClass" },
423424
{ "isTemplate" },

compiler/src/dmd/traits.d

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,27 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
443443
});
444444
}
445445

446+
if (e.ident == Id.isOverlapped)
447+
{
448+
if (dim != 1)
449+
return dimError(1);
450+
451+
return isDeclX((d)
452+
{
453+
auto v = d.isVarDeclaration();
454+
if (!v || !v.isField())
455+
return false;
456+
457+
// Ensure semantic analysis is complete
458+
if (auto agg = v.toParent().isAggregateDeclaration())
459+
{
460+
if (agg.semanticRun < PASS.semanticdone)
461+
agg.dsymbolSemantic(null);
462+
}
463+
464+
return v.overlapped;
465+
});
466+
}
446467
if (e.ident == Id.isArithmetic)
447468
{
448469
return isTypeX(t => t.isIntegral() || t.isFloating());
@@ -2388,7 +2409,7 @@ private void traitNotFound(TraitsExp e)
23882409
initialized = true; // lazy initialization
23892410

23902411
// All possible traits
2391-
__gshared Identifier*[59] idents =
2412+
__gshared Identifier*[60] idents =
23922413
[
23932414
&Id.allMembers,
23942415
&Id.child,
@@ -2420,6 +2441,7 @@ private void traitNotFound(TraitsExp e)
24202441
&Id.identifier,
24212442
&Id.isAbstractClass,
24222443
&Id.isAbstractFunction,
2444+
&Id.isOverlapped,
24232445
&Id.isArithmetic,
24242446
&Id.isAssociativeArray,
24252447
&Id.isCopyable,
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// REQUIRED_ARGS:
2+
3+
struct S1
4+
{
5+
int a;
6+
union
7+
{
8+
int x;
9+
float y;
10+
}
11+
int b;
12+
}
13+
14+
// Anonymous union fields are overlapped
15+
static assert(__traits(isOverlapped, S1.x));
16+
static assert(__traits(isOverlapped, S1.y));
17+
// Regular fields are not overlapped
18+
static assert(!__traits(isOverlapped, S1.a));
19+
static assert(!__traits(isOverlapped, S1.b));
20+
21+
// Named union as a field type
22+
struct S2
23+
{
24+
union U
25+
{
26+
int x;
27+
float y;
28+
}
29+
U u;
30+
}
31+
// The instance 'u' itself is not overlapped (it's a regular field)
32+
static assert(!__traits(isOverlapped, S2.u));
33+
static assert(__traits(isOverlapped, S2.U.x));
34+
static assert(__traits(isOverlapped, S2.U.y));
35+
36+
// Named union accessed directly - its fields are overlapped
37+
union NamedUnion
38+
{
39+
int x;
40+
float y;
41+
}
42+
static assert(__traits(isOverlapped, NamedUnion.x));
43+
static assert(__traits(isOverlapped, NamedUnion.y));
44+
45+
// Anonymous struct (not a union) - fields are NOT overlapped
46+
struct S3
47+
{
48+
struct
49+
{
50+
int x;
51+
float y;
52+
}
53+
}
54+
static assert(!__traits(isOverlapped, S3.x));
55+
static assert(!__traits(isOverlapped, S3.y));
56+
57+
// Named union with nested anonymous union
58+
struct S4
59+
{
60+
union Outer
61+
{
62+
int x; // This IS overlapped (it's in a named union)
63+
// Anonymous union nested inside the named union Outer
64+
union
65+
{
66+
int nested1;
67+
float nested2;
68+
}
69+
}
70+
Outer outer;
71+
}
72+
static assert(!__traits(isOverlapped, S4.outer)); // regular field, not overlapped
73+
static assert(__traits(isOverlapped, S4.Outer.nested1)); // in union (both named and anonymous)
74+
static assert(__traits(isOverlapped, S4.Outer.nested2)); // in union (both named and anonymous)
75+
static assert(__traits(isOverlapped, S4.Outer.x)); // in named union - NOW true!
76+
77+
// Class with anonymous union
78+
class C1
79+
{
80+
union
81+
{
82+
int x;
83+
float y;
84+
}
85+
}
86+
static assert(__traits(isOverlapped, C1.x));
87+
static assert(__traits(isOverlapped, C1.y));
88+
89+
// Non-field arguments should return false (not error)
90+
static assert(!__traits(isOverlapped, S1)); // aggregate type
91+
static assert(!__traits(isOverlapped, int)); // type
92+
static assert(!__traits(isOverlapped, 42)); // literal value
93+
94+
// Deeply nested anonymous unions within anonymous structs
95+
struct S5
96+
{
97+
union
98+
{
99+
int x;
100+
struct
101+
{
102+
union
103+
{
104+
int y;
105+
float z;
106+
}
107+
}
108+
}
109+
}
110+
static assert(__traits(isOverlapped, S5.x)); // outer anonymous union
111+
static assert(__traits(isOverlapped, S5.y)); // inner anonymous union
112+
static assert(__traits(isOverlapped, S5.z)); // inner anonymous union
113+
114+
// Union as top-level type
115+
union TopLevelUnion1
116+
{
117+
int a;
118+
float b;
119+
struct
120+
{
121+
int c;
122+
int d;
123+
}
124+
}
125+
static assert(__traits(isOverlapped, TopLevelUnion1.a));
126+
static assert(__traits(isOverlapped, TopLevelUnion1.b));
127+
static assert(__traits(isOverlapped, TopLevelUnion1.c));
128+
static assert(!__traits(isOverlapped, TopLevelUnion1.d));
129+
130+
union TopLevelUnion2
131+
{
132+
int a;
133+
double b;
134+
struct
135+
{
136+
int c;
137+
int d; // Now d is overlapped with b
138+
}
139+
}
140+
static assert(__traits(isOverlapped, TopLevelUnion2.a));
141+
static assert(__traits(isOverlapped, TopLevelUnion2.b));
142+
static assert(__traits(isOverlapped, TopLevelUnion2.c));
143+
static assert(__traits(isOverlapped, TopLevelUnion2.d));
144+
145+
// Bug #22621
146+
struct Bug22621
147+
{
148+
struct D
149+
{
150+
void* ptr;
151+
}
152+
153+
union
154+
{
155+
struct
156+
{
157+
D d;
158+
}
159+
uint b;
160+
}
161+
}
162+
static assert(__traits(isOverlapped, Bug22621.b));
163+
// Bug #22621: DMD correctly detects that d is overlapped, but the destructor
164+
// logic doesn't properly handle it - it calls d's destructor even when only b was initialized
165+
static assert(__traits(isOverlapped, Bug22621.d)); // Correctly detected!
166+
167+
// Struct with both overlapped and non-overlapped fields
168+
struct S6
169+
{
170+
int regular1;
171+
union
172+
{
173+
int overlapped1;
174+
float overlapped2;
175+
}
176+
int regular2;
177+
}
178+
static assert(!__traits(isOverlapped, S6.regular1));
179+
static assert(__traits(isOverlapped, S6.overlapped1));
180+
static assert(__traits(isOverlapped, S6.overlapped2));
181+
static assert(!__traits(isOverlapped, S6.regular2));
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
TEST_OUTPUT:
3+
---
4+
fail_compilation/test_isOverlapped_errors.d(100): Error: expected 1 arguments for `isOverlapped` but had 0
5+
fail_compilation/test_isOverlapped_errors.d(101): Error: expected 1 arguments for `isOverlapped` but had 2
6+
---
7+
*/
8+
9+
struct S
10+
{
11+
union { int x; }
12+
int y;
13+
}
14+
15+
#line 100
16+
enum a = __traits(isOverlapped);
17+
enum b = __traits(isOverlapped, S, S.x);

0 commit comments

Comments
 (0)