[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