Skip to content

Commit c203d97

Browse files
authored
fix dlang#20157: importC does not handle nested struct designated initializer (dlang#21883)
1 parent a8035fc commit c203d97

3 files changed

Lines changed: 140 additions & 5 deletions

File tree

compiler/src/dmd/initsem.d

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -820,20 +820,105 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
820820
{
821821
DesigInit di = ci.initializerList[index];
822822
Designators* dlist = di.designatorList;
823+
VarDeclaration field;
823824
if (dlist)
824825
{
825826
const length = (*dlist).length;
827+
auto id = (*dlist)[0].ident;
826828
if (length == 0 || !(*dlist)[0].ident)
827829
{
828830
error(ci.loc, "`.identifier` expected for C struct field initializer `%s`", toChars(ci));
829831
return err();
830832
}
833+
831834
if (length > 1)
832835
{
833-
error(ci.loc, "only 1 designator currently allowed for C struct field initializer `%s`", toChars(ci));
834-
return err();
836+
StructDeclaration nstsd = sd; // use this for member structs we wish to traverse
837+
auto subsi = si;
838+
/*
839+
* run this for each designator in the chain until you hit the last
840+
* then perform semantic analysis on the last field in the chain using the previous struct initializer
841+
*/
842+
for (size_t i = 0; i < length; i++)
843+
{
844+
int found;
845+
id = (*dlist)[i].ident;
846+
foreach (f; nstsd.fields[])
847+
{
848+
if (f.ident == id)
849+
{
850+
field = f;
851+
++found;
852+
break;
853+
}
854+
}
855+
if (!found)
856+
{
857+
error(ci.loc, "`.%s` is not a field of `%s`\n", id.toChars(), nstsd.toChars());
858+
return err();
859+
}
860+
861+
auto base = field.type.toBasetype();
862+
863+
if (i >= length -1)
864+
{
865+
subsi.addInit(id, di.initializer);
866+
++index;
867+
continue Loop1;
868+
}
869+
870+
auto tstr = base.isTypeStruct();
871+
auto tarr = base.isTypeSArray();
872+
873+
if (tstr)
874+
{
875+
if (!overlaps(field, nstsd.fields[], subsi))
876+
{
877+
auto innersi = new StructInitializer(ci.loc);
878+
subsi.addInit(id, innersi);
879+
subsi = innersi;
880+
}
881+
else {
882+
foreach(k, ident; subsi.field[])
883+
{
884+
if (ident == id && subsi.value[k])
885+
subsi = subsi.value[k].isStructInitializer();
886+
}
887+
}
888+
nstsd = tstr.sym;
889+
}
890+
/*
891+
* once we hit an array, check & attach the array initializer to the struct initializer
892+
* move to the next initializer id and run initializer semantics on it
893+
*/
894+
else if (tarr)
895+
{
896+
/*
897+
* so tempting to check for null cases for field._init.
898+
* but if your object is set to null on decl, you can't use designators anymore
899+
* and D does well to default initialize for us
900+
*/
901+
auto ai = field._init.isArrayInitializer();
902+
903+
if (ai is null)
904+
{
905+
ai = new ArrayInitializer(ci.loc);
906+
subsi.addInit(id, ai);
907+
field._init = ai;
908+
}
909+
910+
auto ndx = (*dlist)[i+1].exp;
911+
ai.addInit(ndx, di.initializer);
912+
++index;
913+
continue Loop1;
914+
}
915+
else
916+
{
917+
error(ci.loc, "only 1 designated initializer allowed for C struct field of type `%s`", toChars(base));
918+
return err();
919+
}
920+
}
835921
}
836-
auto id = (*dlist)[0].ident;
837922
foreach (k, f; sd.fields[]) // linear search for now
838923
{
839924
if (f.ident == id)
@@ -909,7 +994,6 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
909994
continue Loop1;
910995
}
911996

912-
VarDeclaration field;
913997
while (1) // skip field if it overlaps with previously seen fields
914998
{
915999
field = sd.fields[fieldi];

compiler/test/fail_compilation/init1.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
---
33
fail_compilation/init1.c(100): Error: array initializer has 4 elements, but array length is 3
44
fail_compilation/init1.c(103): Error: `.identifier` expected for C struct field initializer `{[0]=3}`
5-
fail_compilation/init1.c(104): Error: only 1 designator currently allowed for C struct field initializer `{.a[0]=3}`
5+
fail_compilation/init1.c(104): Error: only 1 designated initializer allowed for C struct field of type `int`
66
fail_compilation/init1.c(106): Error: `[ constant-expression ]` expected for C array element initializer `{.a=3}`
77
fail_compilation/init1.c(107): Error: only 1 designator currently allowed for C array element initializer `{[0][1]=3}`
88
fail_compilation/init1.c(110): Error: overlapping initialization for field `a` and `b`
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// https://github.com/dlang/dmd/issues/20157
2+
// testing C intializers using designators
3+
4+
#include <assert.h>
5+
6+
struct top
7+
{
8+
int a;
9+
int b;
10+
};
11+
12+
union t_union
13+
{
14+
int f;
15+
int g;
16+
};
17+
18+
struct Foo
19+
{
20+
int x;
21+
int y;
22+
struct top f;
23+
};
24+
25+
struct Bar
26+
{
27+
struct Foo b;
28+
int arr[3];
29+
union t_union u;
30+
};
31+
32+
struct Bar test = {
33+
.b.x = 5,
34+
.b.y = 7,
35+
.b.f = {8, 9},
36+
.arr[0] = 10,
37+
.arr[1] = 11,
38+
.u.f = 13
39+
};
40+
41+
int main()
42+
{
43+
assert(test.b.x == 5);
44+
assert(test.b.y == 7);
45+
assert(test.b.f.a == 8);
46+
assert(test.b.f.b == 9);
47+
assert(test.arr[0] == 10);
48+
assert(test.arr[1] == 11);
49+
assert(test.u.f == 13);
50+
assert(test.u.g == 13);
51+
}

0 commit comments

Comments
 (0)