Skip to content
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

Merged
merged 7 commits into from
Feb 8, 2024

Conversation

rnk
Copy link
Collaborator

@rnk rnk commented Feb 3, 2024

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 .

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Feb 3, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Feb 3, 2024

@llvm/pr-subscribers-clang

Author: Reid Kleckner (rnk)

Changes

This 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:

  • (modified) clang/lib/AST/Expr.cpp (+12)
  • (modified) clang/test/SemaCXX/compound-literal.cpp (+20)
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}}
Copy link
Collaborator

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?

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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

@@ -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.
Copy link
Collaborator

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.

Copy link
Collaborator

@shafik shafik left a 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.

@rnk
Copy link
Collaborator Author

rnk commented Feb 6, 2024

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.

Copy link

github-actions bot commented Feb 6, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@shafik
Copy link
Collaborator

shafik commented Feb 8, 2024

@AaronBallman since this involves compound literals a C-ism, I would like you to review this as well.

Copy link
Collaborator

@AaronBallman AaronBallman left a 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).

clang/lib/AST/Expr.cpp Outdated Show resolved Hide resolved
Copy link
Collaborator Author

@rnk rnk left a 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.

@rnk rnk merged commit 3c42e10 into llvm:main Feb 8, 2024
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants