[clang] [clang][CodeGen] Emit file-scope compound literals in a constant context (PR #221390)

Akash Manna via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 21 21:54:08 PDT 2026


================
@@ -7643,23 +7643,48 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
   //  "If the compound literal occurs outside the body of a function, the
   //  initializer list shall consist of constant expressions."
   if (IsFileScope)
-    if (auto ILE = dyn_cast<InitListExpr>(LiteralExpr))
+    if (auto ILE = dyn_cast<InitListExpr>(LiteralExpr)) {
+      // An element with an immediate call or source_location is left for the
+      // use site, and for its rebuild too when the rebuilt default arguments
+      // could not be given the use-site location.
+      bool DeferImmediate =
+          isCheckingDefaultArgumentOrInitializer() ||
+          (currentEvaluationContext().DelayedDefaultInitializationContext &&
+           !OutermostDeclarationWithDelayedImmediateInvocations());
       for (unsigned i = 0, j = ILE->getNumInits(); i != j; i++) {
         Expr *Init = ILE->getInit(i);
-        if (!Init->isTypeDependent() && !Init->isValueDependent() &&
-            !Init->isConstantInitializer(Context)) {
+        if (Init->isTypeDependent() || Init->isValueDependent()) {
+          ILE->setInit(i, ConstantExpr::Create(Context, Init));
+          continue;
+        }
+        if (DeferImmediate) {
+          ImmediateCallVisitor V(Context);
+          V.TraverseStmt(Init);
+          if (V.HasImmediateCalls) {
+            ILE->setInit(i, ConstantExpr::Create(Context, Init));
+            continue;
+          }
+        }
+
+        Expr::EvalResult Eval;
+        if (!Init->EvaluateAsConstantInitializer(Eval, Context)) {
           Diag(Init->getExprLoc(), diag::err_init_element_not_constant)
               << Init->getSourceBitField();
           return ExprError();
         }
-
-        ILE->setInit(i, ConstantExpr::Create(Context, Init));
+        // Store the value so CodeGen does not re-evaluate the element outside
+        // a constant context; an immediate invocation already is a
+        // ConstantExpr.
+        if (!isa<ConstantExpr>(Init))
+          ILE->setInit(i, ConstantExpr::Create(Context, Init, Eval.Val));
       }
+    }
 
   auto *E = new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType, VK,
                                               LiteralExpr, IsFileScope);
   if (IsFileScope) {
-    if (!LiteralExpr->isTypeDependent() &&
+    // The elements of an initializer list were checked above.
+    if (!isa<InitListExpr>(LiteralExpr) && !LiteralExpr->isTypeDependent() &&
----------------
akash-manna-sky wrote:

Yes, in C++: a class with a constructor gives a `CXXTemporaryObjectExpr`, and an aggregate initialized from an element of its own type gives a `CXXConstructExpr`. In C it is always a list. A non-list initializer is now checked and stored the same way as a single element, so `CheckForConstantInitializer` is gone from here.

https://github.com/llvm/llvm-project/pull/221390


More information about the cfe-commits mailing list