Fix #20499 - [ImportC] typedef struct with name as a pointer cannot be used with struct name (#21232)

Fixes https://github.com/dlang/dmd/issues/20499
Fixes https://github.com/dlang/dmd/issues/20963

ImportC deferred declaring "tagged" types (structs/unions/enums)
until after it saw a possible typedef so that the identifier for
a typedef declaration like:

    typedef struct { int x; } Foo;

would give the struct the name Foo. In several circumstances,
this led to tagged types not being declared. Resolve this by
chasing down those circumstances.

Also, there were other circumstances where types weren't being
correctly declared which caused other issues. Lock those down.
This commit is contained in:
drpriver 2025-04-15 00:31:21 -07:00 committed by GitHub
parent c30def82bd
commit f4ca164257
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 456 additions and 68 deletions

View file

@ -1720,6 +1720,34 @@ final class CParser(AST) : Parser!AST
/********************************* Declaration Parser ***************************/
//{
void declareTag(AST.TypeTag tt, ref Specifier specifier, bool generate_enum_id = false)
{
if (!tt.id && (generate_enum_id || tt.tok != TOK.enum_)) tt.id = Identifier.generateId("__tag");
/* `struct tag;` and `struct tag { ... };`
* always result in a declaration in the current scope
*/
auto stag = (tt.tok == TOK.struct_) ? new AST.StructDeclaration(tt.loc, tt.id, false) :
(tt.tok == TOK.union_) ? new AST.UnionDeclaration(tt.loc, tt.id) :
new AST.EnumDeclaration(tt.loc, tt.id, tt.base);
if (!tt.alignExps && !tt.packalign.isUnknown())
{
// saw `struct __declspec(align(N)) Tag ...`
auto st = stag.isStructDeclaration();
st.alignment = tt.packalign;
}
stag.members = tt.members;
tt.members = null;
if (!symbols)
symbols = new AST.Dsymbols();
AST.Dsymbol stags = stag;
if (tt.alignExps)
{
auto decls = new AST.Dsymbols(1);
(*decls)[0] = stags;
stags = new AST.AlignDeclaration(stags.loc, tt.alignExps, decls);
}
symbols.push(stags);
}
/*************************************
* C11 6.7
* declaration:
@ -1767,34 +1795,9 @@ final class CParser(AST) : Parser!AST
specifier.packalign = this.packalign;
auto tspec = cparseDeclarationSpecifiers(level, specifier);
AST.Dsymbol declareTag(AST.TypeTag tt, ref Specifier specifier)
{
/* `struct tag;` and `struct tag { ... };`
* always result in a declaration in the current scope
*/
auto stag = (tt.tok == TOK.struct_) ? new AST.StructDeclaration(tt.loc, tt.id, false) :
(tt.tok == TOK.union_) ? new AST.UnionDeclaration(tt.loc, tt.id) :
new AST.EnumDeclaration(tt.loc, tt.id, tt.base);
if (!tt.alignExps && !tt.packalign.isUnknown())
{
// saw `struct __declspec(align(N)) Tag ...`
auto st = stag.isStructDeclaration();
st.alignment = tt.packalign;
}
stag.members = tt.members;
tt.members = null;
if (!symbols)
symbols = new AST.Dsymbols();
AST.Dsymbol stags = stag;
if (tt.alignExps)
{
auto decls = new AST.Dsymbols(1);
(*decls)[0] = stags;
stags = new AST.AlignDeclaration(stags.loc, tt.alignExps, decls);
}
symbols.push(stags);
return stags;
}
// Defer declaring a tagged type (struct/union/enum) so
// that anonymous types can use a typedef as their id.
/* If a declarator does not follow, it is unnamed
*/
@ -1820,12 +1823,13 @@ final class CParser(AST) : Parser!AST
!tt.id && (tt.tok == TOK.struct_ || tt.tok == TOK.union_))
return; // legal but meaningless empty declaration, ignore it
auto stags = declareTag(tt, specifier);
if (tt.members || tt.tok != TOK.enum_)
declareTag(tt, specifier);
if (0 && tt.tok == TOK.enum_) // C11 proscribes enums with no members, but we allow it
{
if (!tt.members)
error(tt.loc, "`enum %s` has no members", stags.toChars());
error(tt.loc, "`enum %s` has no members", tt.toChars());
}
return;
}
@ -1838,6 +1842,15 @@ final class CParser(AST) : Parser!AST
return;
}
if (auto tt = tspec.isTypeTag())
{
if (tt.id && tt.members)
{
// Valid tag decl with name and members, go ahead and declare it now.
declareTag(tt, specifier);
}
}
if (tspec && specifier.mod & MOD.xconst)
{
tspec = toConst(tspec);
@ -1954,7 +1967,7 @@ final class CParser(AST) : Parser!AST
typedefTab.setDim(typedefTabLengthSave);
symbols = symbolsSave;
if (!symbols)
symbols = new AST.Dsymbols; // lazilly create it
symbols = new AST.Dsymbols; // lazily create it
if (level != LVL.global && !tspec && !specifier.scw && !specifier.mod)
error("declaration-specifier-seq required");
@ -1975,38 +1988,33 @@ final class CParser(AST) : Parser!AST
bool isalias = true;
Identifier idt;
if (auto ts = dt.isTypeStruct())
{
if (ts.sym.isAnonymous())
{
// This is a typedef for an anonymous struct-or-union.
// Directly set the ident for the struct-or-union.
ts.sym.ident = id;
isalias = false;
}
idt = ts.sym.ident;
}
else if (auto te = dt.isTypeEnum())
{
if (te.sym.isAnonymous())
{
// This is a typedef for an anonymous enum.
te.sym.ident = id;
isalias = false;
}
idt = te.sym.ident;
}
else if (auto tt = dt.isTypeTag())
if (auto tt = dt.isTypeTag())
{
if (!tt.id && id)
/* This applies for enums declared as
* typedef enum {A} E;
* Or for similar structs and unions.
*/
tt.id = id;
Specifier spec;
declareTag(tt, spec);
if (tt.members)
{
Specifier spec;
declareTag(tt, spec);
}
idt = tt.id;
}
else if (auto tt = tspec.isTypeTag())
{
// The unusual situation of an anonymous typedef struct where the
// first typedef can't be used as its name.
// Just declare it now so we can get a valid id.
if (!tt.id && tt.members)
{
Specifier spec;
declareTag(tt, spec, true);
idt = tt.id;
}
}
if (isalias)
{
//printf("AliasDeclaration %s %s\n", id.toChars(), dt.toChars());
@ -2035,6 +2043,16 @@ final class CParser(AST) : Parser!AST
if (dt.ty == AST.Tvoid)
error("`void` has no value");
if (auto tt = tspec.isTypeTag())
{
// Anonymous struct being used as a var decl type
if (!tt.id && tt.members && tt.tok != TOK.enum_)
{
Specifier spec;
declareTag(tt, spec);
}
}
AST.Initializer initializer;
bool hasInitializer;
if (token.value == TOK.assign)
@ -4097,7 +4115,7 @@ final class CParser(AST) : Parser!AST
* struct { ... members ... };
* C11 6.7.2.1-13
*/
if (!tt.id && tt.members)
if (!tt.id && tt.members && tt.tok != TOK.enum_)
{
/* members of anonymous struct are considered members of
* the containing struct
@ -4113,17 +4131,19 @@ final class CParser(AST) : Parser!AST
/* `struct tag;` and `struct tag { ... };`
* always result in a declaration in the current scope
*/
// TODO: merge in specifier
auto stag = (tt.tok == TOK.struct_)
? new AST.StructDeclaration(tt.loc, tt.id, false)
: new AST.UnionDeclaration(tt.loc, tt.id);
stag.members = tt.members;
if (!symbols)
symbols = new AST.Dsymbols();
auto s = applySpecifier(stag, specifier);
symbols.push(s);
Specifier spec;
declareTag(tt, spec);
return;
}
if (auto tt = tspec.isTypeTag())
{
// Declare the tagged type at this point.
if (tt.members)
{
Specifier spec;
declareTag(tt, spec);
}
}
while (1)
{

View file

@ -0,0 +1,8 @@
// https://github.com/dlang/dmd/issues/20499
typedef struct Foo {
int x;
} *pFoo;
struct Bar {
struct Foo foo;
};

View file

@ -0,0 +1,61 @@
// https://github.com/dlang/dmd/issues/20499
struct Outer {
struct __attribute__((aligned(8))) {
int x;
} n;
enum {A};
enum {B} b;
};
struct Outer2 {
struct __attribute__((aligned(8))) Nested {
int x;
} n;
};
const int x = A;
const int y = B;
struct Outer o = {3};
_Static_assert(_Alignof(typeof(o.n)) == 8, "");
_Static_assert(_Alignof(struct Outer) == 8, "");
_Static_assert(_Alignof(struct Outer2) == 8, "");
_Static_assert(_Alignof(struct Nested) == 8, "");
void test(void){
struct Outer {
struct __attribute__((aligned(16))) {
int x;
} n;
enum {A=2};
enum {B=3} b;
};
struct Outer2 {
struct __attribute__((aligned(16))) Nested {
int x;
} n;
};
const int x = A;
const int y = B;
struct Outer o = {3};
_Static_assert(_Alignof(typeof(o.n)) == 16, "");
_Static_assert(_Alignof(struct Outer) == 16, "");
_Static_assert(_Alignof(struct Outer2) == 16, "");
_Static_assert(_Alignof(struct Nested) == 16, "");
}
void test2(void){
const int x = A;
const int y = B;
struct Outer o = {3};
_Static_assert(_Alignof(typeof(o.n)) == 8, "");
_Static_assert(_Alignof(struct Outer) == 8, "");
_Static_assert(_Alignof(struct Outer2) == 8, "");
_Static_assert(_Alignof(struct Nested) == 8, "");
}

View file

@ -0,0 +1,287 @@
// https://github.com/dlang/dmd/issues/20499
//
// attribute((aligned())) is so we can tell if attributes are being applied.
//
typedef struct __attribute__((aligned(8))) S {
int x, y;
} S;
_Static_assert(sizeof(struct S) == 8, "sizeof(S)");
_Static_assert(_Alignof(struct S) == 8, "_Alignof(S)");
typedef struct __attribute__((aligned(8))) Foo {
int x, y;
} *pFoo, Foo, FooB;
_Static_assert(sizeof(struct Foo) == sizeof(struct S), "sizeof(Foo)");
_Static_assert(_Alignof(struct Foo) == _Alignof(struct S), "_Alignof(Foo)");
_Static_assert(sizeof(Foo) == sizeof(struct S), "sizeof(Foo)");
_Static_assert(_Alignof(Foo) == _Alignof(struct S), "_Alignof(Foo)");
_Static_assert(sizeof(FooB) == sizeof(struct S), "sizeof(FooB)");
_Static_assert(_Alignof(FooB) == _Alignof(struct S), "_Alignof(FooB)");
pFoo pf;
_Static_assert(sizeof(*pf) == sizeof(struct S), "sizeof(*pf)");
_Static_assert(_Alignof(typeof(*pf)) == _Alignof(struct S), "_Alignof(*pf)");
typedef struct __attribute__((aligned(8))) {
int x, y;
} Baz, *pBaz, BazB;
_Static_assert(sizeof(Baz) == sizeof(struct S), "sizeof(Baz)");
_Static_assert(sizeof(BazB) == sizeof(struct S), "sizeof(BazB)");
_Static_assert(_Alignof(Baz) == _Alignof(struct S), "_Alignof(Baz)");
_Static_assert(_Alignof(BazB) == _Alignof(struct S), "_Alignof(BazB)");
pBaz pb;
_Static_assert(sizeof(*pb) == sizeof(struct S), "sizeof(*pb)");
_Static_assert(_Alignof(typeof(*pb)) == _Alignof(struct S), "_Alignof(*pb)");
typedef struct __attribute__((aligned(8))) {
int x, y;
} *pTux;
pTux pt;
_Static_assert(sizeof(*pt) == sizeof(struct S), "sizeof(*pt)");
_Static_assert(_Alignof(typeof(*pt)) == _Alignof(struct S), "_Alignof(*pt)");
typedef struct __attribute__((aligned(8))) {
int x, y;
} Qux;
_Static_assert(sizeof(Qux) == sizeof(struct S), "sizeof(FooB)");
_Static_assert(_Alignof(Qux) == _Alignof(struct S), "_Alignof(FooB)");
struct Bar {
struct S foo;
};
_Static_assert(sizeof(struct Bar) == sizeof(struct S), "sizeof(Bar)");
_Static_assert(_Alignof(struct Bar) == _Alignof(struct S), "_Alignof(Bar)");
typedef struct __attribute__((aligned(8))) {
int x, y;
} *pLux;
pLux pl;
_Static_assert(sizeof(*pl) == sizeof(struct S), "sizeof(*pl)");
_Static_assert(_Alignof(typeof(*pl)) == _Alignof(struct S), "_Alignof(*pl)");
typedef struct __attribute__((aligned(8))) {
int x, y;
} ****pWux;
pWux pw;
_Static_assert(sizeof(****pw) == sizeof(struct S), "sizeof(****pw)");
_Static_assert(_Alignof(typeof(****pw)) == _Alignof(struct S), "_Alignof(****pw)");
struct __attribute__((aligned(8))) {
int x, y;
} f;
_Static_assert(sizeof(f) == sizeof(struct S), "sizeof(f)");
_Static_assert(_Alignof(typeof(f)) == _Alignof(struct S), "_Alignof(f)");
struct __attribute__((aligned(8))) {
int x, y;
} fa[3];
_Static_assert(sizeof(fa[0]) == sizeof(struct S), "sizeof(fa[0])");
_Static_assert(_Alignof(typeof(fa[0])) == _Alignof(struct S), "_Alignof(fa[0])");
void locals(void){
// function local version
// Use different values so we know we aren't just using globals
typedef struct __attribute__((aligned(16))) S {
int x, y[7];
} S;
_Static_assert(sizeof(struct S) == 32, "sizeof(S)");
_Static_assert(_Alignof(struct S) == 16, "_Alignof(S)");
typedef struct __attribute__((aligned(16))) Foo {
int x, y[7];
} *pFoo, Foo, FooB;
_Static_assert(sizeof(struct Foo) == sizeof(struct S), "sizeof(Foo)");
_Static_assert(_Alignof(struct Foo) == _Alignof(struct S), "_Alignof(Foo)");
_Static_assert(sizeof(Foo) == sizeof(struct S), "sizeof(Foo)");
_Static_assert(_Alignof(Foo) == _Alignof(struct S), "_Alignof(Foo)");
_Static_assert(sizeof(FooB) == sizeof(struct S), "sizeof(FooB)");
_Static_assert(_Alignof(FooB) == _Alignof(struct S), "_Alignof(FooB)");
pFoo pf;
_Static_assert(sizeof(*pf) == sizeof(struct S), "sizeof(*pf)");
_Static_assert(_Alignof(typeof(*pf)) == _Alignof(struct S), "_Alignof(*pf)");
typedef struct __attribute__((aligned(16))) {
int x, y[7];
} Baz, *pBaz, BazB;
_Static_assert(sizeof(Baz) == sizeof(struct S), "sizeof(Baz)");
_Static_assert(sizeof(BazB) == sizeof(struct S), "sizeof(BazB)");
_Static_assert(_Alignof(Baz) == _Alignof(struct S), "_Alignof(Baz)");
_Static_assert(_Alignof(BazB) == _Alignof(struct S), "_Alignof(BazB)");
pBaz pb;
_Static_assert(sizeof(*pb) == sizeof(struct S), "sizeof(*pb)");
_Static_assert(_Alignof(typeof(*pb)) == _Alignof(struct S), "_Alignof(*pb)");
typedef struct __attribute__((aligned(16))) {
int x, y[7];
} *pTux;
pTux pt;
_Static_assert(sizeof(*pt) == sizeof(struct S), "sizeof(*pt)");
_Static_assert(_Alignof(typeof(*pt)) == _Alignof(struct S), "_Alignof(*pt)");
typedef struct __attribute__((aligned(16))) {
int x, y[7];
} Qux;
_Static_assert(sizeof(Qux) == sizeof(struct S), "sizeof(FooB)");
_Static_assert(_Alignof(Qux) == _Alignof(struct S), "_Alignof(FooB)");
struct Bar {
struct S foo;
};
_Static_assert(sizeof(struct Bar) == sizeof(struct S), "sizeof(Bar)");
_Static_assert(_Alignof(struct Bar) == _Alignof(struct S), "_Alignof(Bar)");
typedef struct __attribute__((aligned(16))) {
int x, y[7];
} *pLux;
pLux pl;
_Static_assert(sizeof(*pl) == sizeof(struct S), "sizeof(*pl)");
_Static_assert(_Alignof(typeof(*pl)) == _Alignof(struct S), "_Alignof(*pl)");
typedef struct __attribute__((aligned(16))) {
int x, y[7];
} ****pWux;
pWux pw;
_Static_assert(sizeof(****pw) == sizeof(struct S), "sizeof(****pw)");
_Static_assert(_Alignof(typeof(****pw)) == _Alignof(struct S), "_Alignof(****pw)");
struct __attribute__((aligned(16))) {
int x, y[7];
} f;
_Static_assert(sizeof(f) == sizeof(struct S), "sizeof(f)");
_Static_assert(_Alignof(typeof(f)) == _Alignof(struct S), "_Alignof(f)");
// Verify shadowing works
{
typedef struct __attribute__((aligned(32))) S {
int x, y[15];
} S;
_Static_assert(sizeof(struct S) == 64, "sizeof(S)");
_Static_assert(_Alignof(struct S) == 32, "_Alignof(S)");
typedef struct __attribute__((aligned(32))) Foo {
int x, y[15];
} *pFoo, Foo, FooB;
_Static_assert(sizeof(struct Foo) == sizeof(struct S), "sizeof(Foo)");
_Static_assert(_Alignof(struct Foo) == _Alignof(struct S), "_Alignof(Foo)");
_Static_assert(sizeof(Foo) == sizeof(struct S), "sizeof(Foo)");
_Static_assert(_Alignof(Foo) == _Alignof(struct S), "_Alignof(Foo)");
_Static_assert(sizeof(FooB) == sizeof(struct S), "sizeof(FooB)");
_Static_assert(_Alignof(FooB) == _Alignof(struct S), "_Alignof(FooB)");
pFoo pf;
_Static_assert(sizeof(*pf) == sizeof(struct S), "sizeof(*pf)");
_Static_assert(_Alignof(typeof(*pf)) == _Alignof(struct S), "_Alignof(*pf)");
typedef struct __attribute__((aligned(32))) {
int x, y[15];
} Baz, *pBaz, BazB;
_Static_assert(sizeof(Baz) == sizeof(struct S), "sizeof(Baz)");
_Static_assert(sizeof(BazB) == sizeof(struct S), "sizeof(BazB)");
_Static_assert(_Alignof(Baz) == _Alignof(struct S), "_Alignof(Baz)");
_Static_assert(_Alignof(BazB) == _Alignof(struct S), "_Alignof(BazB)");
pBaz pb;
_Static_assert(sizeof(*pb) == sizeof(struct S), "sizeof(*pb)");
_Static_assert(_Alignof(typeof(*pb)) == _Alignof(struct S), "_Alignof(*pb)");
typedef struct __attribute__((aligned(32))) {
int x, y[15];
} *pTux;
pTux pt;
_Static_assert(sizeof(*pt) == sizeof(struct S), "sizeof(*pt)");
_Static_assert(_Alignof(typeof(*pt)) == _Alignof(struct S), "_Alignof(*pt)");
typedef struct __attribute__((aligned(32))) {
int x, y[15];
} Qux;
_Static_assert(sizeof(Qux) == sizeof(struct S), "sizeof(FooB)");
_Static_assert(_Alignof(Qux) == _Alignof(struct S), "_Alignof(FooB)");
struct Bar {
struct S foo;
};
_Static_assert(sizeof(struct Bar) == sizeof(struct S), "sizeof(Bar)");
_Static_assert(_Alignof(struct Bar) == _Alignof(struct S), "_Alignof(Bar)");
typedef struct __attribute__((aligned(32))) {
int x, y[15];
} *pLux;
pLux pl;
_Static_assert(sizeof(*pl) == sizeof(struct S), "sizeof(*pl)");
_Static_assert(_Alignof(typeof(*pl)) == _Alignof(struct S), "_Alignof(*pl)");
typedef struct __attribute__((aligned(32))) {
int x, y[15];
} ****pWux;
pWux pw;
_Static_assert(sizeof(****pw) == sizeof(struct S), "sizeof(****pw)");
_Static_assert(_Alignof(typeof(****pw)) == _Alignof(struct S), "_Alignof(****pw)");
struct __attribute__((aligned(32))) {
int x, y[15];
} f;
_Static_assert(sizeof(f) == sizeof(struct S), "sizeof(f)");
_Static_assert(_Alignof(typeof(f)) == _Alignof(struct S), "_Alignof(f)");
}
}
void globals(void){
_Static_assert(sizeof(struct S) == 8, "sizeof(S)");
_Static_assert(_Alignof(struct S) == 8, "_Alignof(S)");
_Static_assert(sizeof(struct Foo) == sizeof(struct S), "sizeof(Foo)");
_Static_assert(_Alignof(struct Foo) == _Alignof(struct S), "_Alignof(Foo)");
_Static_assert(sizeof(Foo) == sizeof(struct S), "sizeof(Foo)");
_Static_assert(_Alignof(Foo) == _Alignof(struct S), "_Alignof(Foo)");
_Static_assert(sizeof(FooB) == sizeof(struct S), "sizeof(FooB)");
_Static_assert(_Alignof(FooB) == _Alignof(struct S), "_Alignof(FooB)");
pFoo pf;
_Static_assert(sizeof(*pf) == sizeof(struct S), "sizeof(*pf)");
_Static_assert(_Alignof(typeof(*pf)) == _Alignof(struct S), "_Alignof(*pf)");
_Static_assert(sizeof(Baz) == sizeof(struct S), "sizeof(Baz)");
_Static_assert(sizeof(BazB) == sizeof(struct S), "sizeof(BazB)");
_Static_assert(_Alignof(Baz) == _Alignof(struct S), "_Alignof(Baz)");
_Static_assert(_Alignof(BazB) == _Alignof(struct S), "_Alignof(BazB)");
pBaz pb;
_Static_assert(sizeof(*pb) == sizeof(struct S), "sizeof(*pb)");
_Static_assert(_Alignof(typeof(*pb)) == _Alignof(struct S), "_Alignof(*pb)");
pTux pt;
_Static_assert(sizeof(*pt) == sizeof(struct S), "sizeof(*pt)");
_Static_assert(_Alignof(typeof(*pt)) == _Alignof(struct S), "_Alignof(*pt)");
_Static_assert(sizeof(Qux) == sizeof(struct S), "sizeof(FooB)");
_Static_assert(_Alignof(Qux) == _Alignof(struct S), "_Alignof(FooB)");
_Static_assert(sizeof(struct Bar) == sizeof(struct S), "sizeof(Bar)");
_Static_assert(_Alignof(struct Bar) == _Alignof(struct S), "_Alignof(Bar)");
pLux pl;
_Static_assert(sizeof(*pl) == sizeof(struct S), "sizeof(*pl)");
_Static_assert(_Alignof(typeof(*pl)) == _Alignof(struct S), "_Alignof(*pl)");
pWux pw;
_Static_assert(sizeof(****pw) == sizeof(struct S), "sizeof(****pw)");
_Static_assert(_Alignof(typeof(****pw)) == _Alignof(struct S), "_Alignof(****pw)");
Foo foo = {1, 2, 3};
struct Foo foo2 = {1, 2, 3};
}

View file

@ -0,0 +1,2 @@
// https://github.com/dlang/dmd/issues/20499
import imports.imp20499;

View file

@ -0,0 +1,12 @@
// https://github.com/dlang/dmd/issues/20963
void cdind() {
struct ABC {
int y;
} *p;
{
struct ABC { int x; } abc;
abc.x = 1;
}
}

View file

@ -1,7 +1,7 @@
// check importAll analysis of C files
/* TEST_OUTPUT:
---
fail_compilation/failcstuff3.c(54): Error: union `failcstuff3.S22061` conflicts with struct `failcstuff3.S22061` at fail_compilation/failcstuff3.c(50)
fail_compilation/failcstuff3.c(54): Error: redeclaration of `S22061`
---
*/

View file

@ -1242,7 +1242,6 @@ void cdind()
assert(cbptr->nextread == 4);
}
#if 0 // TODO ImportC
{ struct ABC { char a,b,*l_text; } abc;
static struct { int w_doto; struct ABC *w_dotp; } curw,*curwp;
@ -1253,7 +1252,6 @@ void cdind()
c = curwp->w_dotp->l_text[curwp->w_doto]&0xFF;
assert(c == '2');
}
#endif
}
void logexp()