nickdesaulniers created this revision. nickdesaulniers added reviewers: rsmith, aaron.ballman. nickdesaulniers requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
When declaring an anonymous struct with `const` or `volatile` qualifiers, ensure the members of that anonymous struct are created with that qualifier. GCC behaves this way, and WG14 is currently discussing this behavior. See also: - https://bugs.llvm.org/show_bug.cgi?id=48755 - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98826 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D95408 Files: clang/lib/Sema/SemaDecl.cpp clang/test/AST/ast-dump-decl.c clang/test/AST/ast-dump-records.c clang/test/Sema/struct-decl-anonymous-members.c clang/test/Sema/struct-decl.c
Index: clang/test/Sema/struct-decl.c =================================================================== --- clang/test/Sema/struct-decl.c +++ clang/test/Sema/struct-decl.c @@ -110,3 +110,8 @@ struct FlexibleArrayMem a; // expected-warning {{field 'a' with variable sized type 'struct FlexibleArrayMem' not at the end of a struct or class is a GNU extension}} struct {}; }; + +struct QualifiedAnonymousMembers { + const struct { int foo; }; + volatile struct { int bar; }; +}; Index: clang/test/Sema/struct-decl-anonymous-members.c =================================================================== --- /dev/null +++ clang/test/Sema/struct-decl-anonymous-members.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 %s -fsyntax-only -verify +struct dummy { + const struct { // expected-note {{non-static data member '' declared const here}} + int a; + // 'b' here should be 'const volatile' qualified. + volatile struct { int b; }; + }; + int c; +}; + +extern void fn(const int *); // expected-note {{passing argument to parameter here}} +extern void fn2(volatile int *); // expected-note {{passing argument to parameter here}} + +void test(struct dummy *a) { + fn(&a->b); // expected-warning {{passing 'const volatile int *' to parameter of type 'const int *' discards qualifiers}} + fn2(&a->b); // expected-warning {{passing 'const volatile int *' to parameter of type 'volatile int *' discards qualifiers}} + a->a = a->c; // expected-error {{cannot assign to non-static data member '' with const-qualified type}} +} Index: clang/test/AST/ast-dump-records.c =================================================================== --- clang/test/AST/ast-dump-records.c +++ clang/test/AST/ast-dump-records.c @@ -156,3 +156,24 @@ // CHECK-NEXT: Field 0x{{[^ ]*}} 'f' 'int' }; +struct H { + const struct { + int bar; + }; + volatile struct { + int baz; + }; +}; +// CHECK: RecordDecl 0x{{[^ ]*}} <line:[[@LINE-8]]:1, line:[[@LINE-1]]:1> line:[[@LINE-8]]:8 struct H definition +// CHECK-NEXT: RecordDecl 0x{{[^ ]*}} <line:[[@LINE-8]]:9, line:[[@LINE-6]]:3> line:[[@LINE-8]]:9 struct definition +// CHECK-NEXT: FieldDecl 0x{{[^ ]*}} <line:[[@LINE-8]]:5, col:9> col:9 bar 'int' +// CHECK-NEXT: FieldDecl 0x{{[^ ]*}} <line:[[@LINE-10]]:3, col:9> col:9 implicit 'const struct H::(anonymous at {{.*}}:[[@LINE-10]]:9)' +// CHECK-NEXT: IndirectFieldDecl 0x{{[^ ]*}} <line:[[@LINE-10]]:9> col:9 implicit bar 'int' +// CHECK-NEXT: Field 0x{{[^ ]*}} '' 'const struct H::(anonymous at {{.*}}:[[@LINE-12]]:9)' +// CHECK-NEXT: Field 0x{{[^ ]*}} 'bar' 'int' +// CHECK-NEXT: RecordDecl 0x{{[^ ]*}} <line:[[@LINE-11]]:12, line:[[@LINE-9]]:3> line:[[@LINE-11]]:12 struct definition +// CHECK-NEXT: FieldDecl 0x{{[^ ]*}} <line:[[@LINE-11]]:5, col:9> col:9 baz 'int' +// CHECK-NEXT: FieldDecl 0x{{[^ ]*}} <line:[[@LINE-13]]:3, col:12> col:12 implicit 'volatile struct H::(anonymous at {{.*}}:[[@LINE-13]]:12)' +// CHECK-NEXT: IndirectFieldDecl 0x{{[^ ]*}} <line:[[@LINE-13]]:9> col:9 implicit baz 'int' +// CHECK-NEXT: Field 0x{{[^ ]*}} '' 'volatile struct H::(anonymous at {{.*}}:[[@LINE-15]]:12)' +// CHECK-NEXT: Field 0x{{[^ ]*}} 'baz' 'int' Index: clang/test/AST/ast-dump-decl.c =================================================================== --- clang/test/AST/ast-dump-decl.c +++ clang/test/AST/ast-dump-decl.c @@ -118,11 +118,24 @@ struct { int TestIndirectFieldDecl; }; + const struct { + int Test48755Const; + }; + volatile struct { + int Test48755Volatile; + }; }; // CHECK: IndirectFieldDecl{{.*}} TestIndirectFieldDecl 'int' // CHECK-NEXT: Field{{.*}} '' // CHECK-NEXT: Field{{.*}} 'TestIndirectFieldDecl' +// CHECK: IndirectFieldDecl{{.*}} Test48755Const 'int' +// CHECK-NEXT: Field{{.*}} 'const struct testIndirectFieldDecl::{{.*}}' + +// CHECK: IndirectFieldDecl{{.*}} Test48755Volatile 'int' +// CHECK-NEXT: Field{{.*}} 'volatile struct testIndirectFieldDecl::{{.*}}' + + // FIXME: It would be nice to dump the enum and its enumerators. int TestFunctionDecl(int x, enum { e } y) { return x; Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -5176,11 +5176,18 @@ // Create a declaration for this anonymous struct/union. NamedDecl *Anon = nullptr; if (RecordDecl *OwningClass = dyn_cast<RecordDecl>(Owner)) { - Anon = FieldDecl::Create( - Context, OwningClass, DS.getBeginLoc(), Record->getLocation(), - /*IdentifierInfo=*/nullptr, Context.getTypeDeclType(Record), TInfo, - /*BitWidth=*/nullptr, /*Mutable=*/false, - /*InitStyle=*/ICIS_NoInit); + QualType QT = Context.getTypeDeclType(Record); + if (!getLangOpts().CPlusPlus) { + if (DS.getTypeQualifiers() & DeclSpec::TQ_const) + QT.addConst(); + if (DS.getTypeQualifiers() & DeclSpec::TQ_volatile) + QT.addVolatile(); + } + Anon = FieldDecl::Create(Context, OwningClass, DS.getBeginLoc(), + Record->getLocation(), + /*IdentifierInfo=*/nullptr, QT, TInfo, + /*BitWidth=*/nullptr, /*Mutable=*/false, + /*InitStyle=*/ICIS_NoInit); Anon->setAccess(AS); ProcessDeclAttributes(S, Anon, Dc);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits