https://github.com/andykaylor updated 
https://github.com/llvm/llvm-project/pull/203402

>From b43575ab8f1e26bb26808e38463b540bca79446e Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Mon, 8 Jun 2026 16:20:58 -0700
Subject: [PATCH 1/2] [CIR] Implement handling for CXXConstructLValue
 expressions

This implements the handling to emit an l-value for CXXConstructExpr
and CXXTemporaryObjectExpr expressions. This is a simple copy from
the equivalent code in classic codegen and uses existing CIR code for
most of the actual work.
---
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp          |  8 ++
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp      |  4 +-
 clang/lib/CIR/CodeGen/CIRGenFunction.h        |  1 +
 .../test/CIR/CodeGen/cxx-construct-lvalue.cpp | 73 +++++++++++++++++++
 4 files changed, 83 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index e110f6e5d21a2..beaedd853f57b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -2145,6 +2145,14 @@ CIRGenFunction::emitCXXBindTemporaryLValue(const 
CXXBindTemporaryExpr *e) {
   return makeAddrLValue(slot.getAddress(), e->getType(), 
AlignmentSource::Decl);
 }
 
+LValue CIRGenFunction::emitCXXConstructLValue(const CXXConstructExpr *e) {
+  assert(e->getType()->getAsCXXRecordDecl()->hasTrivialDestructor() &&
+         "binding l-value to type which needs a temporary");
+  AggValueSlot slot = createAggTemp(e->getType(), getLoc(e->getSourceRange()));
+  emitCXXConstructExpr(e, slot);
+  return makeAddrLValue(slot.getAddress(), e->getType(), 
AlignmentSource::Decl);
+}
+
 LValue CIRGenFunction::emitBinaryOperatorLValue(const BinaryOperator *e) {
   // Comma expressions just emit their LHS then their RHS as an l-value.
   if (e->getOpcode() == BO_Comma) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 21b2640b36071..4b020c96964a7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1220,9 +1220,7 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
     return emitInitListLValue(cast<InitListExpr>(e));
   case Expr::CXXTemporaryObjectExprClass:
   case Expr::CXXConstructExprClass:
-    getCIRGenModule().errorNYI(e->getSourceRange(),
-                               "emitLValue: CXXConstructExpr");
-    return LValue();
+    return emitCXXConstructLValue(cast<CXXConstructExpr>(e));
   case Expr::CXXBindTemporaryExprClass:
     return emitCXXBindTemporaryLValue(cast<CXXBindTemporaryExpr>(e));
   case Expr::CXXUuidofExprClass:
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 35112d1dd7b68..317151c8d61c6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1755,6 +1755,7 @@ class CIRGenFunction : public CIRGenTypeCache {
   LValue emitCallExprLValue(const clang::CallExpr *e);
 
   LValue emitCXXBindTemporaryLValue(const CXXBindTemporaryExpr *e);
+  LValue emitCXXConstructLValue(const CXXConstructExpr *e);
   CIRGenCallee emitCallee(const clang::Expr *e);
 
   template <typename T>
diff --git a/clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp 
b/clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp
new file mode 100644
index 0000000000000..0ca10c15e16b9
--- /dev/null
+++ b/clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp
@@ -0,0 +1,73 @@
+// RUN: %clang_cc1 -std=c++03 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -std=c++03 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -std=c++03 -triple x86_64-unknown-linux-gnu -emit-llvm %s 
-o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
+
+// A multi-argument constructor call written with explicit type syntax produces
+// a CXXTemporaryObjectExpr. Using it as the base of a member access reaches
+// emitLValue with that expression class.
+struct Pt {
+  Pt(int a, int b);
+  int v;
+};
+
+int tempObj(int i) { return Pt(i, i).v; }
+
+// CIR-LABEL: cir.func {{.*}}@_Z7tempObji(%arg0: !s32i
+// CIR:         %[[I:.*]] = cir.alloca "i"
+// CIR:         %[[RET:.*]] = cir.alloca "__retval"
+// CIR:         %[[TMP:.*]] = cir.alloca "tmp"
+// CIR:         cir.store %arg0, %[[I]]
+// CIR:         %[[A:.*]] = cir.load align(4) %[[I]]
+// CIR:         %[[B:.*]] = cir.load align(4) %[[I]]
+// CIR:         cir.call @_ZN2PtC1Eii(%[[TMP]], %[[A]], %[[B]])
+// CIR:         %[[V:.*]] = cir.get_member %[[TMP]][0] {name = "v"} : 
!cir.ptr<!rec_Pt> -> !cir.ptr<!s32i>
+// CIR:         %[[VAL:.*]] = cir.load align(4) %[[V]]
+// CIR:         cir.store %[[VAL]], %[[RET]]
+// CIR:         %[[RES:.*]] = cir.load %[[RET]]
+// CIR:         cir.return %[[RES]]
+
+// LLVM-LABEL: define {{.*}}i32 @_Z7tempObji
+// LLVM:         %[[I_ADDR:.*]] = alloca i32
+// LLVM:         %[[TMP:.*]] = alloca %struct.Pt
+// LLVM:         store i32 %{{.*}}, ptr %[[I_ADDR]]
+// LLVM:         %[[A:.*]] = load i32, ptr %[[I_ADDR]]
+// LLVM:         %[[B:.*]] = load i32, ptr %[[I_ADDR]]
+// LLVM:         call void @_ZN2PtC1Eii(ptr {{.*}} %[[TMP]], i32 {{.*}} 
%[[A]], i32 {{.*}} %[[B]])
+// LLVM:         %[[V:.*]] = getelementptr inbounds nuw %struct.Pt, ptr 
%[[TMP]], i32 0, i32 0
+// LLVM:         %[[VAL:.*]] = load i32, ptr %[[V]]
+
+// A single-argument constructor call performs a constructor conversion, so the
+// base of the member access is a CXXFunctionalCastExpr whose subexpression is 
a
+// CXXConstructExpr. emitCastLValue forwards to the subexpression, reaching
+// emitLValue with the CXXConstructExpr class.
+struct Conv {
+  Conv(int a);
+  int y;
+};
+
+int construct(int i) { return Conv(i).y; }
+
+// CIR-LABEL: cir.func {{.*}}@_Z9constructi(%arg0: !s32i
+// CIR:         %[[I:.*]] = cir.alloca "i"
+// CIR:         %[[RET:.*]] = cir.alloca "__retval"
+// CIR:         %[[TMP:.*]] = cir.alloca "tmp"
+// CIR:         cir.store %arg0, %[[I]]
+// CIR:         %[[A:.*]] = cir.load align(4) %[[I]]
+// CIR:         cir.call @_ZN4ConvC1Ei(%[[TMP]], %[[A]])
+// CIR:         %[[Y:.*]] = cir.get_member %[[TMP]][0] {name = "y"} : 
!cir.ptr<!rec_Conv> -> !cir.ptr<!s32i>
+// CIR:         %[[VAL:.*]] = cir.load align(4) %[[Y]]
+// CIR:         cir.store %[[VAL]], %[[RET]]
+// CIR:         %[[RES:.*]] = cir.load %[[RET]]
+// CIR:         cir.return %[[RES]]
+
+// LLVM-LABEL: define {{.*}}i32 @_Z9constructi
+// LLVM:         %[[I_ADDR:.*]] = alloca i32
+// LLVM:         %[[TMP:.*]] = alloca %struct.Conv
+// LLVM:         store i32 %{{.*}}, ptr %[[I_ADDR]]
+// LLVM:         %[[A:.*]] = load i32, ptr %[[I_ADDR]]
+// LLVM:         call void @_ZN4ConvC1Ei(ptr {{.*}} %[[TMP]], i32 {{.*}} 
%[[A]])
+// LLVM:         %[[Y:.*]] = getelementptr inbounds nuw %struct.Conv, ptr 
%[[TMP]], i32 0, i32 0
+// LLVM:         %[[VAL:.*]] = load i32, ptr %[[Y]]

>From 90ef516f5fe1c2325e60c6094c40312678bda271 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Fri, 12 Jun 2026 09:32:05 -0700
Subject: [PATCH 2/2] Address review feedback

---
 clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp 
b/clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp
index 0ca10c15e16b9..51d8be2b417aa 100644
--- a/clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp
+++ b/clang/test/CIR/CodeGen/cxx-construct-lvalue.cpp
@@ -5,9 +5,6 @@
 // RUN: %clang_cc1 -std=c++03 -triple x86_64-unknown-linux-gnu -emit-llvm %s 
-o %t.ll
 // RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
 
-// A multi-argument constructor call written with explicit type syntax produces
-// a CXXTemporaryObjectExpr. Using it as the base of a member access reaches
-// emitLValue with that expression class.
 struct Pt {
   Pt(int a, int b);
   int v;
@@ -39,10 +36,6 @@ int tempObj(int i) { return Pt(i, i).v; }
 // LLVM:         %[[V:.*]] = getelementptr inbounds nuw %struct.Pt, ptr 
%[[TMP]], i32 0, i32 0
 // LLVM:         %[[VAL:.*]] = load i32, ptr %[[V]]
 
-// A single-argument constructor call performs a constructor conversion, so the
-// base of the member access is a CXXFunctionalCastExpr whose subexpression is 
a
-// CXXConstructExpr. emitCastLValue forwards to the subexpression, reaching
-// emitLValue with the CXXConstructExpr class.
 struct Conv {
   Conv(int a);
   int y;

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to