nickdesaulniers created this revision. nickdesaulniers added reviewers: eli.friedman, aaron.ballman, rsmith. Herald added a project: clang. Herald added a subscriber: cfe-commits. nickdesaulniers updated this revision to Diff 250046. nickdesaulniers added a comment. nickdesaulniers marked an inline comment as done. nickdesaulniers added subscribers: void, jyknight. nickdesaulniers marked an inline comment as not done.
- add 2 missing CHECKs ================ Comment at: clang/lib/AST/Expr.cpp:3164 + const QualType &QT = cast<DeclRefExpr>(this)->getDecl()->getType(); + if (QT->isStructureType() && QT.isConstQualified()) + return true; ---------------- Interesting, playing with this more in godbolt, it looks like the struct doesn't even have to be const qualified. ================ Comment at: clang/lib/AST/Expr.cpp:3164 + const QualType &QT = cast<DeclRefExpr>(this)->getDecl()->getType(); + if (QT->isStructureType() && QT.isConstQualified()) + return true; ---------------- nickdesaulniers wrote: > Interesting, playing with this more in godbolt, it looks like the struct > doesn't even have to be const qualified. Or, rather, behaves differently between C and C++ mode; C -> const required C++ -> const not required This seems to be an undocumented GNU C extension. For code like: struct foo { ... }; struct bar { struct foo foo; }; struct foo my_foo = { ... }; struct bar my_bar = { .foo = my_foo }; during CodeGen of LLVM IR, copy the initializer of `my_foo` into the initializer of `my_bar` recursively. Eli Friedman points out the relevant part of the C standard seems to have some flexibility in what is considered a constant expression: 6.6 paragraph 10: An implementation may accept other forms of constant expressions. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D76096 Files: clang/lib/AST/Expr.cpp clang/lib/CodeGen/CGExprConstant.cpp clang/test/CodeGen/const-init.c clang/test/Sema/init.c Index: clang/test/Sema/init.c =================================================================== --- clang/test/Sema/init.c +++ clang/test/Sema/init.c @@ -160,3 +160,22 @@ typedef struct { uintptr_t x : 2; } StructWithBitfield; StructWithBitfield bitfieldvar = { (uintptr_t)&bitfieldvar }; // expected-error {{initializer element is not a compile-time constant}} + +// PR45157 +struct PR4517_foo {}; +struct PR4517_bar { + struct PR4517_foo foo; +}; +const struct PR4517_foo my_foo = {}; +struct PR4517_bar my_bar = { + .foo = my_foo, // no-warning +}; +struct PR4517_bar my_bar2 = (struct PR4517_bar){ + .foo = my_foo, // no-warning +}; +struct PR4517_bar my_bar3 = { + my_foo, // no-warning +}; +struct PR4517_bar my_bar4 = (struct PR4517_bar){ + my_foo // no-warning +}; Index: clang/test/CodeGen/const-init.c =================================================================== --- clang/test/CodeGen/const-init.c +++ clang/test/CodeGen/const-init.c @@ -181,3 +181,20 @@ #pragma pack() // CHECK: @g31.a = internal global %struct.anon.2 { i16 23122, i32 -12312731, i16 -312 }, align 4 } + +struct PR4517_foo { + int x; +}; +struct PR4517_bar { + struct PR4517_foo foo; +}; +const struct PR4517_foo my_foo = {.x = 42}; +struct PR4517_bar my_bar = {.foo = my_foo}; +struct PR4517_bar my_bar2 = (struct PR4517_bar){.foo = my_foo}; +struct PR4517_bar my_bar3 = {my_foo}; +struct PR4517_bar my_bar4 = (struct PR4517_bar){my_foo}; +// CHECK: @my_foo = constant %struct.PR4517_foo { i32 42 }, align 4 +// CHECK: @my_bar = global %struct.PR4517_bar { %struct.PR4517_foo { i32 42 } }, align 4 +// CHECK: @my_bar2 = global %struct.PR4517_bar { %struct.PR4517_foo { i32 42 } }, align 4 +// CHECK: @my_bar3 = global %struct.PR4517_bar { %struct.PR4517_foo { i32 42 } }, align 4 +// CHECK: @my_bar4 = global %struct.PR4517_bar { %struct.PR4517_foo { i32 42 } }, align 4 Index: clang/lib/CodeGen/CGExprConstant.cpp =================================================================== --- clang/lib/CodeGen/CGExprConstant.cpp +++ clang/lib/CodeGen/CGExprConstant.cpp @@ -1007,6 +1007,13 @@ return Visit(PE->getSubExpr(), T); } + llvm::Constant *VisitDeclRefExpr(DeclRefExpr *DRE, QualType T) { + if (VarDecl *V = dyn_cast<VarDecl>(DRE->getDecl())) + if (V->hasInit()) + return Visit(V->getInit(), V->getType()); + return nullptr; + } + llvm::Constant * VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *PE, QualType T) { Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ clang/lib/AST/Expr.cpp @@ -3158,6 +3158,13 @@ switch (getStmtClass()) { default: break; + case DeclRefExprClass: + if (!Ctx.getLangOpts().CPlusPlus && Ctx.getLangOpts().GNUMode) { + const QualType &QT = cast<DeclRefExpr>(this)->getDecl()->getType(); + if (QT->isStructureType() && QT.isConstQualified()) + return true; + } + break; case StringLiteralClass: case ObjCEncodeExprClass: return true;
Index: clang/test/Sema/init.c =================================================================== --- clang/test/Sema/init.c +++ clang/test/Sema/init.c @@ -160,3 +160,22 @@ typedef struct { uintptr_t x : 2; } StructWithBitfield; StructWithBitfield bitfieldvar = { (uintptr_t)&bitfieldvar }; // expected-error {{initializer element is not a compile-time constant}} + +// PR45157 +struct PR4517_foo {}; +struct PR4517_bar { + struct PR4517_foo foo; +}; +const struct PR4517_foo my_foo = {}; +struct PR4517_bar my_bar = { + .foo = my_foo, // no-warning +}; +struct PR4517_bar my_bar2 = (struct PR4517_bar){ + .foo = my_foo, // no-warning +}; +struct PR4517_bar my_bar3 = { + my_foo, // no-warning +}; +struct PR4517_bar my_bar4 = (struct PR4517_bar){ + my_foo // no-warning +}; Index: clang/test/CodeGen/const-init.c =================================================================== --- clang/test/CodeGen/const-init.c +++ clang/test/CodeGen/const-init.c @@ -181,3 +181,20 @@ #pragma pack() // CHECK: @g31.a = internal global %struct.anon.2 { i16 23122, i32 -12312731, i16 -312 }, align 4 } + +struct PR4517_foo { + int x; +}; +struct PR4517_bar { + struct PR4517_foo foo; +}; +const struct PR4517_foo my_foo = {.x = 42}; +struct PR4517_bar my_bar = {.foo = my_foo}; +struct PR4517_bar my_bar2 = (struct PR4517_bar){.foo = my_foo}; +struct PR4517_bar my_bar3 = {my_foo}; +struct PR4517_bar my_bar4 = (struct PR4517_bar){my_foo}; +// CHECK: @my_foo = constant %struct.PR4517_foo { i32 42 }, align 4 +// CHECK: @my_bar = global %struct.PR4517_bar { %struct.PR4517_foo { i32 42 } }, align 4 +// CHECK: @my_bar2 = global %struct.PR4517_bar { %struct.PR4517_foo { i32 42 } }, align 4 +// CHECK: @my_bar3 = global %struct.PR4517_bar { %struct.PR4517_foo { i32 42 } }, align 4 +// CHECK: @my_bar4 = global %struct.PR4517_bar { %struct.PR4517_foo { i32 42 } }, align 4 Index: clang/lib/CodeGen/CGExprConstant.cpp =================================================================== --- clang/lib/CodeGen/CGExprConstant.cpp +++ clang/lib/CodeGen/CGExprConstant.cpp @@ -1007,6 +1007,13 @@ return Visit(PE->getSubExpr(), T); } + llvm::Constant *VisitDeclRefExpr(DeclRefExpr *DRE, QualType T) { + if (VarDecl *V = dyn_cast<VarDecl>(DRE->getDecl())) + if (V->hasInit()) + return Visit(V->getInit(), V->getType()); + return nullptr; + } + llvm::Constant * VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *PE, QualType T) { Index: clang/lib/AST/Expr.cpp =================================================================== --- clang/lib/AST/Expr.cpp +++ clang/lib/AST/Expr.cpp @@ -3158,6 +3158,13 @@ switch (getStmtClass()) { default: break; + case DeclRefExprClass: + if (!Ctx.getLangOpts().CPlusPlus && Ctx.getLangOpts().GNUMode) { + const QualType &QT = cast<DeclRefExpr>(this)->getDecl()->getType(); + if (QT->isStructureType() && QT.isConstQualified()) + return true; + } + break; case StringLiteralClass: case ObjCEncodeExprClass: return true;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits