-
Notifications
You must be signed in to change notification settings - Fork 10.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Consider aggregate bases when checking if an InitListExpr is constant #80519
Conversation
@llvm/pr-subscribers-clang Author: Reid Kleckner (rnk) ChangesThis code was correct as written prior to C++17, which allowed bases to appear in the initializer list. Clang currently requires compound literal initializers at file scope to be constants, which is how I tested this behavior change, but I am open to other testing ideas. This fixes at least one part of issue #80510 . Full diff: https://github.com/llvm/llvm-project/pull/80519.diff 2 Files Affected:
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index d665a08deb47e..8852fadf79b9a 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3342,6 +3342,18 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef,
if (ILE->getType()->isRecordType()) {
unsigned ElementNo = 0;
RecordDecl *RD = ILE->getType()->castAs<RecordType>()->getDecl();
+
+ // Check bases for C++17 aggregate initializers.
+ if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+ for (unsigned i = 0, e = CXXRD->getNumBases(); i < e; i++) {
+ if (ElementNo < ILE->getNumInits()) {
+ const Expr *Elt = ILE->getInit(ElementNo++);
+ if (!Elt->isConstantInitializer(Ctx, false, Culprit))
+ return false;
+ }
+ }
+ }
+
for (const auto *Field : RD->fields()) {
// If this is a union, skip all the fields that aren't being initialized.
if (RD->isUnion() && ILE->getInitializedFieldInUnion() != Field)
diff --git a/clang/test/SemaCXX/compound-literal.cpp b/clang/test/SemaCXX/compound-literal.cpp
index 5957099de53af..81f8b41ff0313 100644
--- a/clang/test/SemaCXX/compound-literal.cpp
+++ b/clang/test/SemaCXX/compound-literal.cpp
@@ -3,6 +3,7 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify -ast-dump %s > %t-11
// RUN: FileCheck --input-file=%t-11 %s
// RUN: FileCheck --input-file=%t-11 %s --check-prefix=CHECK-CXX11
+// RUN: %clang_cc1 -verify -std=c++17 %s
// http://llvm.org/PR7905
namespace PR7905 {
@@ -108,3 +109,22 @@ int computed_with_lambda = [] {
return result;
}();
#endif
+
+#if __cplusplus >= 201703L
+namespace DynamicFileScopeLiteral {
+// This covers the case where we have a file-scope compound literal with a
+// non-constant initializer in C++. Previously, we had a bug where Clang forgot
+// to consider initializer list elements for bases.
+struct Empty {};
+struct Foo : Empty {
+ int x;
+ int y;
+};
+int f();
+Foo o = (Foo){
+ {},
+ 1,
+ f() // expected-error {{initializer element is not a compile-time constant}}
+};
+}
+#endif
|
Foo o = (Foo){ | ||
{}, | ||
1, | ||
f() // expected-error {{initializer element is not a compile-time constant}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gcc accepts this: https://godbolt.org/z/enWrG56je
Why do we believe this should be rejected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is correct and I did discover that in my testing, but I think fixing that goes beyond the scope of this patch. Clang currently rejects this case if you remove the empty base: https://godbolt.org/z/1x8hshM75 Adding support for non-constant compound literals at file scope requires codegen changes, and this is fixing a small bug to make more isConstantInitializer
correct.
The real issue is that I couldn't find a better way to test isConstantInitializer
that will be resilient to that future bug fix, and I'm not sure what to do about that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went ahead and make a targeted unit test for isConstantInitializer
to see if that is nicer
clang/lib/AST/Expr.cpp
Outdated
@@ -3342,6 +3342,18 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, | |||
if (ILE->getType()->isRecordType()) { | |||
unsigned ElementNo = 0; | |||
RecordDecl *RD = ILE->getType()->castAs<RecordType>()->getDecl(); | |||
|
|||
// Check bases for C++17 aggregate initializers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be checking CPlusPlus17
here?
Also I would like to see a standard quote in the comments. I know we are not doing this locally but these are really helpful and we should be doing this more consistently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also in the issue: #80510 you pointed out a crash bug. We should have the bug covered in the test case as well.
I think that is covered by the existing test case, instead of crashing, we now reject with an error. In the future if we want to align with GCC as you suggest, we'll need to have a codegen test case for non-constant compound literals of aggregates with bases. |
✅ With the latest revision this PR passed the C/C++ code formatter. |
@AaronBallman since this involves compound literals a C-ism, I would like you to review this as well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please be sure to add a release note so users know about the fix, but aside from a bad standards reference, this LGTM (no concerns about C behavior either).
Co-authored-by: Aaron Ballman <aaron@aaronballman.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I added notes.
This code was correct as written prior to C++17, which allowed bases to appear in the initializer list.
This was observable by creating non-constant aggregate initialization at file scope in a compound literal, but since that behavior will change soon if we implement support for dynamic initialization, I also added a unit test for
isConstantInitializer
.This fixes at least one part of issue #80510 .