[clang] [clang][bytecode] Skip rvalue subobject adjustments for global temporaries (PR #189044)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Fri Mar 27 09:25:23 PDT 2026


https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/189044

>From 2876664ca0d8eb2df8ed6cd2f5681c5ef1352cec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Fri, 27 Mar 2026 16:37:05 +0100
Subject: [PATCH] rvalue adjustments

---
 clang/lib/AST/ByteCode/Compiler.cpp           | 57 ++++++++++++-------
 clang/lib/AST/ByteCode/EvalEmitter.cpp        | 11 +++-
 clang/lib/AST/ByteCode/Program.cpp            |  5 +-
 clang/lib/AST/ByteCode/Program.h              |  2 +-
 .../reference-temporary-subobject.cpp         |  2 +
 .../static-local-in-local-class.cpp           |  1 +
 6 files changed, 49 insertions(+), 29 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 4d129a7ccd497..aa3aa6854247d 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -3205,22 +3205,37 @@ bool Compiler<Emitter>::VisitExprWithCleanups(const ExprWithCleanups *E) {
 template <class Emitter>
 bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
     const MaterializeTemporaryExpr *E) {
-  const Expr *SubExpr = E->getSubExpr();
-
   if (Initializing) {
     // We already have a value, just initialize that.
-    return this->delegate(SubExpr);
+    return this->delegate(E->getSubExpr());
   }
   // If we don't end up using the materialized temporary anyway, don't
   // bother creating it.
   if (DiscardResult)
-    return this->discard(SubExpr);
+    return this->discard(E->getSubExpr());
+
+  SmallVector<const Expr *, 2> CommaLHSs;
+  SmallVector<SubobjectAdjustment, 2> Adjustments;
+  const Expr *Inner;
+  if (!Ctx.getLangOpts().CPlusPlus11)
+    Inner =
+        E->getSubExpr()->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments);
+  else
+    Inner = E->getSubExpr();
+
+  // If we passed any comma operators, evaluate their LHSs.
+  for (const Expr *LHS : CommaLHSs) {
+    if (!this->discard(LHS))
+      return false;
+  }
+
+  // FIXME: Find a test case where Adjustments matters.
 
   // When we're initializing a global variable *or* the storage duration of
   // the temporary is explicitly static, create a global variable.
-  OptPrimType SubExprT = classify(SubExpr);
+  OptPrimType InnerT = classify(Inner);
   if (E->getStorageDuration() == SD_Static) {
-    UnsignedOrNone GlobalIndex = P.createGlobal(E);
+    UnsignedOrNone GlobalIndex = P.createGlobal(E, Inner->getType());
     if (!GlobalIndex)
       return false;
 
@@ -3228,20 +3243,20 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
         E->getLifetimeExtendedTemporaryDecl();
     assert(TempDecl);
 
-    if (SubExprT) {
-      if (!this->visit(SubExpr))
+    if (InnerT) {
+      if (!this->visit(Inner))
         return false;
-      if (!this->emitInitGlobalTemp(*SubExprT, *GlobalIndex, TempDecl, E))
+      if (!this->emitInitGlobalTemp(*InnerT, *GlobalIndex, TempDecl, E))
         return false;
       return this->emitGetPtrGlobal(*GlobalIndex, E);
     }
 
-    if (!this->checkLiteralType(SubExpr))
+    if (!this->checkLiteralType(Inner))
       return false;
     // Non-primitive values.
     if (!this->emitGetPtrGlobal(*GlobalIndex, E))
       return false;
-    if (!this->visitInitializer(SubExpr))
+    if (!this->visitInitializer(Inner))
       return false;
     return this->emitInitGlobalTempComp(TempDecl, E);
   }
@@ -3251,27 +3266,26 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
                            : ScopeKind::Block;
 
   // For everyhing else, use local variables.
-  if (SubExprT) {
-    bool IsConst = SubExpr->getType().isConstQualified();
-    bool IsVolatile = SubExpr->getType().isVolatileQualified();
+  if (InnerT) {
+    bool IsConst = Inner->getType().isConstQualified();
+    bool IsVolatile = Inner->getType().isVolatileQualified();
     unsigned LocalIndex =
-        allocateLocalPrimitive(E, *SubExprT, IsConst, IsVolatile, VarScope);
+        allocateLocalPrimitive(E, *InnerT, IsConst, IsVolatile, VarScope);
     if (!this->VarScope->LocalsAlwaysEnabled &&
         !this->emitEnableLocal(LocalIndex, E))
       return false;
 
-    if (!this->visit(SubExpr))
+    if (!this->visit(Inner))
       return false;
-    if (!this->emitSetLocal(*SubExprT, LocalIndex, E))
+    if (!this->emitSetLocal(*InnerT, LocalIndex, E))
       return false;
 
     return this->emitGetPtrLocal(LocalIndex, E);
   }
 
-  if (!this->checkLiteralType(SubExpr))
+  if (!this->checkLiteralType(Inner))
     return false;
 
-  const Expr *Inner = E->getSubExpr()->skipRValueSubobjectAdjustments();
   if (UnsignedOrNone LocalIndex =
           allocateLocal(E, Inner->getType(), VarScope)) {
     InitLinkScope<Emitter> ILS(this, InitLink::Temp(*LocalIndex));
@@ -3282,7 +3296,7 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
 
     if (!this->emitGetPtrLocal(*LocalIndex, E))
       return false;
-    return this->visitInitializer(SubExpr) && this->emitFinishInit(E);
+    return this->visitInitializer(Inner) && this->emitFinishInit(E);
   }
   return false;
 }
@@ -3323,7 +3337,7 @@ bool Compiler<Emitter>::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
     if (T && !E->isLValue())
       return this->delegate(Init);
 
-    UnsignedOrNone GlobalIndex = P.createGlobal(E);
+    UnsignedOrNone GlobalIndex = P.createGlobal(E, E->getType());
     if (!GlobalIndex)
       return false;
 
@@ -5220,7 +5234,6 @@ Compiler<Emitter>::visitVarDecl(const VarDecl *VD, const Expr *Init,
     };
 
     DeclScope<Emitter> LocalScope(this, VD);
-
     UnsignedOrNone GlobalIndex = P.getGlobal(VD);
     if (GlobalIndex) {
       // The global was previously created but the initializer failed.
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp
index 8e8ffc131814b..9cbd786e883a2 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.cpp
+++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp
@@ -11,6 +11,7 @@
 #include "IntegralAP.h"
 #include "Interp.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
 #include "llvm/ADT/ScopeExit.h"
 
 using namespace clang;
@@ -370,12 +371,16 @@ void EvalEmitter::updateGlobalTemporaries() {
     assert(GlobalIndex);
     const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex);
     APValue *Cached = Temp->getOrCreateValue(true);
-    if (OptPrimType T = Ctx.classify(E->getType())) {
+
+    QualType TempType = E->getType();
+    if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E))
+      TempType = MTE->getSubExpr()->skipRValueSubobjectAdjustments()->getType();
+
+    if (OptPrimType T = Ctx.classify(TempType)) {
       TYPE_SWITCH(*T,
                   { *Cached = Ptr.deref<T>().toAPValue(Ctx.getASTContext()); });
     } else {
-      if (std::optional<APValue> APV =
-              Ptr.toRValue(Ctx, Temp->getTemporaryExpr()->getType()))
+      if (std::optional<APValue> APV = Ptr.toRValue(Ctx, TempType))
         *Cached = *APV;
     }
   }
diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp
index ccef38ab14ebe..2ad5879b4e82a 100644
--- a/clang/lib/AST/ByteCode/Program.cpp
+++ b/clang/lib/AST/ByteCode/Program.cpp
@@ -232,10 +232,10 @@ UnsignedOrNone Program::createGlobal(const ValueDecl *VD, const Expr *Init) {
   return *Idx;
 }
 
-UnsignedOrNone Program::createGlobal(const Expr *E) {
+UnsignedOrNone Program::createGlobal(const Expr *E, QualType ExprType) {
   if (auto Idx = getGlobal(E))
     return Idx;
-  if (auto Idx = createGlobal(E, E->getType(), /*IsStatic=*/true,
+  if (auto Idx = createGlobal(E, ExprType, /*IsStatic=*/true,
                               /*IsExtern=*/false, /*IsWeak=*/false)) {
     GlobalIndices[E] = *Idx;
     return *Idx;
@@ -402,7 +402,6 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
                                       bool IsConst, bool IsTemporary,
                                       bool IsMutable, bool IsVolatile,
                                       const Expr *Init) {
-
   // Classes and structures.
   if (const auto *RD = Ty->getAsRecordDecl()) {
     if (const auto *Record = getOrCreateRecord(RD))
diff --git a/clang/lib/AST/ByteCode/Program.h b/clang/lib/AST/ByteCode/Program.h
index c8795504391fa..91126a51e8ddc 100644
--- a/clang/lib/AST/ByteCode/Program.h
+++ b/clang/lib/AST/ByteCode/Program.h
@@ -92,7 +92,7 @@ class Program final {
   UnsignedOrNone createGlobal(const ValueDecl *VD, const Expr *Init);
 
   /// Creates a global from a lifetime-extended temporary.
-  UnsignedOrNone createGlobal(const Expr *E);
+  UnsignedOrNone createGlobal(const Expr *E, QualType ExprType);
 
   /// Creates a new function from a code range.
   template <typename... Ts>
diff --git a/clang/test/CodeGenCXX/reference-temporary-subobject.cpp b/clang/test/CodeGenCXX/reference-temporary-subobject.cpp
index 828bd53692448..b4ff133d4e483 100644
--- a/clang/test/CodeGenCXX/reference-temporary-subobject.cpp
+++ b/clang/test/CodeGenCXX/reference-temporary-subobject.cpp
@@ -10,6 +10,8 @@
 //
 // RUN: %clang_cc1 -std=c++98 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
 // RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -std=c++98 -triple x86_64-linux-gnu -emit-llvm -o - %s -fexperimental-new-constant-interpreter | FileCheck %s
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm -o - %s -fexperimental-new-constant-interpreter | FileCheck %s
 
 // `const int &` bound to a member of an S temporary.
 // The backing store is the whole S object, which can't be stored const.
diff --git a/clang/test/CodeGenCXX/static-local-in-local-class.cpp b/clang/test/CodeGenCXX/static-local-in-local-class.cpp
index dd760b4b529d5..7d2c83eccb7e4 100644
--- a/clang/test/CodeGenCXX/static-local-in-local-class.cpp
+++ b/clang/test/CodeGenCXX/static-local-in-local-class.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -triple x86_64-linux -fblocks -emit-llvm -o - %s -std=c++1y | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-linux -fblocks -emit-llvm -o - %s -std=c++1y -fexperimental-new-constant-interpreter | FileCheck %s
 
 // CHECK: @"_ZZZNK17pr18020_constexpr3$_1clEvENKUlvE_clEvE2l2" =
 // CHECK: internal global ptr @"_ZZNK17pr18020_constexpr3$_1clEvE2l1"



More information about the cfe-commits mailing list