From 4d5a93ee643057791a5234a22c6d19b4b35a9e7b Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Fri, 3 Oct 2025 15:11:12 -0700 Subject: [PATCH 1/2] [CIR] Fix destructor calls with temporary objects This fixes a few problems where destructors were not called for temporary objects and, after calling was enabled, they were placed incorrectly relative to cir.yield operations. --- clang/lib/CIR/CodeGen/CIRGenClass.cpp | 2 +- clang/lib/CIR/CodeGen/CIRGenCleanup.cpp | 6 + clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 30 ++- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 8 +- clang/lib/CIR/CodeGen/CIRGenFunction.h | 3 + clang/lib/CIR/CodeGen/CIRGenValue.h | 7 + clang/test/CIR/CodeGen/dtors.cpp | 173 ++++++++++++++++++ 7 files changed, 220 insertions(+), 9 deletions(-) create mode 100644 clang/test/CIR/CodeGen/dtors.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index 9d12a13dd79c0..8f4377b435775 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -690,7 +690,7 @@ void CIRGenFunction::emitCXXAggrConstructorCall( // every temporary created in a default argument expression is sequenced // before the construction of the next array element, if any. { - assert(!cir::MissingFeatures::runCleanupsScope()); + RunCleanupsScope scope(*this); // Evaluate the constructor and its arguments in a regular // partial-destroy cleanup. diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp index 4d4d10be40024..870069715df22 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp @@ -28,6 +28,12 @@ using namespace clang::CIRGen; // CIRGenFunction cleanup related //===----------------------------------------------------------------------===// +/// Emits all the code to cause the given temporary to be cleaned up. +void CIRGenFunction::emitCXXTemporary(const CXXTemporary *temporary, + QualType tempType, Address ptr) { + pushDestroy(NormalAndEHCleanup, ptr, tempType, destroyCXXObject); +} + //===----------------------------------------------------------------------===// // EHScopeStack //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index 1e987f3bedc7e..828f86966f0cd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -46,6 +46,12 @@ class AggExprEmitter : public StmtVisitor { return dest; } + void ensureDest(mlir::Location loc, QualType ty) { + if (!dest.isIgnored()) + return; + dest = cgf.createAggTemp(ty, loc, "agg.tmp.ensured"); + } + public: AggExprEmitter(CIRGenFunction &cgf, AggValueSlot dest) : cgf(cgf), dest(dest) {} @@ -96,10 +102,22 @@ class AggExprEmitter : public StmtVisitor { Visit(die->getExpr()); } void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *e) { - assert(!cir::MissingFeatures::aggValueSlotDestructedFlag()); + // Ensure that we have a slot, but if we already do, remember + // whether it was externally destructed. + bool wasExternallyDestructed = dest.isExternallyDestructed(); + ensureDest(cgf.getLoc(e->getSourceRange()), e->getType()); + + // We're going to push a destructor if there isn't already one. + dest.setExternallyDestructed(); + Visit(e->getSubExpr()); + + // Push that destructor we promised. + if (!wasExternallyDestructed) + cgf.emitCXXTemporary(e->getTemporary(), e->getType(), dest.getAddress()); } void VisitLambdaExpr(LambdaExpr *e); + void VisitExprWithCleanups(ExprWithCleanups *e); // Stubs -- These should be moved up when they are implemented. void VisitCastExpr(CastExpr *e) { @@ -239,11 +257,6 @@ class AggExprEmitter : public StmtVisitor { cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitCXXStdInitializerListExpr"); } - - void VisitExprWithCleanups(ExprWithCleanups *e) { - cgf.cgm.errorNYI(e->getSourceRange(), - "AggExprEmitter: VisitExprWithCleanups"); - } void VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *e) { cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitCXXScalarValueInitExpr"); @@ -586,6 +599,11 @@ void AggExprEmitter::VisitLambdaExpr(LambdaExpr *e) { } } +void AggExprEmitter::VisitExprWithCleanups(ExprWithCleanups *e) { + CIRGenFunction::RunCleanupsScope cleanups(cgf); + Visit(e->getSubExpr()); +} + void AggExprEmitter::VisitCallExpr(const CallExpr *e) { if (e->getCallReturnType(cgf.getContext())->isReferenceType()) { cgf.cgm.errorNYI(e->getSourceRange(), "reference return type"); diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 500007f6f241b..2a24f72df6800 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -1099,7 +1099,9 @@ class ScalarExprEmitter : public StmtVisitor { CIRGenFunction::LexicalScope lexScope{cgf, loc, b.getInsertionBlock()}; cgf.curLexScope->setAsTernary(); - b.create(loc, cgf.evaluateExprAsBool(e->getRHS())); + mlir::Value res = cgf.evaluateExprAsBool(e->getRHS()); + lexScope.forceCleanup(); + b.create(loc, res); }, /*falseBuilder*/ [&](mlir::OpBuilder &b, mlir::Location loc) { @@ -1151,7 +1153,9 @@ class ScalarExprEmitter : public StmtVisitor { CIRGenFunction::LexicalScope lexScope{cgf, loc, b.getInsertionBlock()}; cgf.curLexScope->setAsTernary(); - b.create(loc, cgf.evaluateExprAsBool(e->getRHS())); + mlir::Value res = cgf.evaluateExprAsBool(e->getRHS()); + lexScope.forceCleanup(); + b.create(loc, res); }); return maybePromoteBoolResult(resOp.getResult(), resTy); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 3eba242550c2c..f4cefc42de197 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1240,6 +1240,9 @@ class CIRGenFunction : public CIRGenTypeCache { RValue emitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *expr); + void emitCXXTemporary(const CXXTemporary *temporary, QualType tempType, + Address ptr); + void emitCXXThrowExpr(const CXXThrowExpr *e); void emitCtorPrologue(const clang::CXXConstructorDecl *ctor, diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h index ea8625a0fbee5..25b6ecb503a6e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenValue.h +++ b/clang/lib/CIR/CodeGen/CIRGenValue.h @@ -371,6 +371,13 @@ class AggValueSlot { mayOverlap, isZeroed); } + IsDestructed_t isExternallyDestructed() const { + return IsDestructed_t(destructedFlag); + } + void setExternallyDestructed(bool destructed = true) { + destructedFlag = destructed; + } + clang::Qualifiers getQualifiers() const { return quals; } Address getAddress() const { return addr; } diff --git a/clang/test/CIR/CodeGen/dtors.cpp b/clang/test/CIR/CodeGen/dtors.cpp new file mode 100644 index 0000000000000..66554b70e1700 --- /dev/null +++ b/clang/test/CIR/CodeGen/dtors.cpp @@ -0,0 +1,173 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -mconstructor-aliases -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -mconstructor-aliases -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -mconstructor-aliases -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG + +struct A { + ~A(); +}; + +void test_temporary_dtor() { + A(); +} + +// CIR: cir.func dso_local @_Z19test_temporary_dtorv() +// CIR: %[[ALLOCA:.*]] = cir.alloca !rec_A, !cir.ptr, ["agg.tmp0"] +// CIR: cir.call @_ZN1AD1Ev(%[[ALLOCA]]) nothrow : (!cir.ptr) -> () + +// LLVM: define dso_local void @_Z19test_temporary_dtorv() +// LLVM: %[[ALLOCA:.*]] = alloca %struct.A, i64 1, align 1 +// LLVM: call void @_ZN1AD1Ev(ptr %[[ALLOCA]]) + +// OGCG: define dso_local void @_Z19test_temporary_dtorv() +// OGCG: %[[ALLOCA:.*]] = alloca %struct.A, align 1 +// OGCG: call void @_ZN1AD1Ev(ptr {{.*}} %[[ALLOCA]]) + +struct B { + int n; + B(int n) : n(n) {} + ~B() {} +}; + +bool make_temp(const B &) { return false; } +bool test_temp_or() { return make_temp(1) || make_temp(2); } + +// CIR: cir.func{{.*}} @_Z12test_temp_orv() +// CIR: %[[SCOPE:.*]] = cir.scope { +// CIR: %[[REF_TMP0:.*]] = cir.alloca !rec_B, !cir.ptr, ["ref.tmp0"] +// CIR: %[[ONE:.*]] = cir.const #cir.int<1> +// CIR: cir.call @_ZN1BC2Ei(%[[REF_TMP0]], %[[ONE]]) +// CIR: %[[MAKE_TEMP0:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP0]]) +// CIR: %[[TERNARY:.*]] = cir.ternary(%[[MAKE_TEMP0]], true { +// CIR: %[[TRUE:.*]] = cir.const #true +// CIR: cir.yield %[[TRUE]] : !cir.bool +// CIR: }, false { +// CIR: %[[REF_TMP1:.*]] = cir.alloca !rec_B, !cir.ptr, ["ref.tmp1"] +// CIR: %[[TWO:.*]] = cir.const #cir.int<2> +// CIR: cir.call @_ZN1BC2Ei(%[[REF_TMP1]], %[[TWO]]) +// CIR: %[[MAKE_TEMP1:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP1]]) +// CIR: cir.call @_ZN1BD2Ev(%[[REF_TMP1]]) +// CIR: cir.yield %[[MAKE_TEMP1]] : !cir.bool +// CIR: }) +// CIR: cir.call @_ZN1BD2Ev(%[[REF_TMP0]]) +// CIR: cir.yield %[[TERNARY]] : !cir.bool +// CIR: } : !cir.bool + +// LLVM: define{{.*}} i1 @_Z12test_temp_orv() { +// LLVM: %[[REF_TMP0:.*]] = alloca %struct.B +// LLVM: %[[REF_TMP1:.*]] = alloca %struct.B +// LLVM: br label %[[LOR_BEGIN:.*]] +// LLVM: [[LOR_BEGIN]]: +// LLVM: call void @_ZN1BC2Ei(ptr %[[REF_TMP0]], i32 1) +// LLVM: %[[MAKE_TEMP0:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP0]]) +// LLVM: br i1 %[[MAKE_TEMP0]], label %[[LHS_TRUE_BLOCK:.*]], label %[[LHS_FALSE_BLOCK:.*]] +// LLVM: [[LHS_TRUE_BLOCK]]: +// LLVM: br label %[[RESULT_BLOCK:.*]] +// LLVM: [[LHS_FALSE_BLOCK]]: +// LLVM: call void @_ZN1BC2Ei(ptr %[[REF_TMP1]], i32 2) +// LLVM: %[[MAKE_TEMP1:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP1]]) +// LLVM: call void @_ZN1BD2Ev(ptr %[[REF_TMP1]]) +// LLVM: br label %[[RESULT_BLOCK]] +// LLVM: [[RESULT_BLOCK]]: +// LLVM: %[[RESULT:.*]] = phi i1 [ %[[MAKE_TEMP1]], %[[LHS_FALSE_BLOCK]] ], [ true, %[[LHS_TRUE_BLOCK]] ] +// LLVM: br label %[[LOR_END:.*]] +// LLVM: [[LOR_END]]: +// LLVM: call void @_ZN1BD2Ev(ptr %[[REF_TMP0]]) + +// OGCG: define {{.*}} i1 @_Z12test_temp_orv() +// OGCG: [[ENTRY:.*]]: +// OGCG: %[[RETVAL:.*]] = alloca i1 +// OGCG: %[[REF_TMP0:.*]] = alloca %struct.B +// OGCG: %[[REF_TMP1:.*]] = alloca %struct.B +// OGCG: %[[CLEANUP_COND:.*]] = alloca i1 +// OGCG: call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP0]], i32 {{.*}} 1) +// OGCG: %[[MAKE_TEMP0:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP0]]) +// OGCG: store i1 false, ptr %cleanup.cond +// OGCG: br i1 %[[MAKE_TEMP0]], label %[[LOR_END:.*]], label %[[LOR_RHS:.*]] +// OGCG: [[LOR_RHS]]: +// OGCG: call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP1]], i32 {{.*}} 2) +// OGCG: store i1 true, ptr %[[CLEANUP_COND]] +// OGCG: %[[MAKE_TEMP1:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP1]]) +// OGCG: br label %[[LOR_END]] +// OGCG: [[LOR_END]]: +// OGCG: %[[PHI:.*]] = phi i1 [ true, %[[ENTRY]] ], [ %[[MAKE_TEMP1]], %[[LOR_RHS]] ] +// OGCG: store i1 %[[PHI]], ptr %[[RETVAL]] +// OGCG: %[[CLEANUP_IS_ACTIVE:.*]] = load i1, ptr %[[CLEANUP_COND]] +// OGCG: br i1 %[[CLEANUP_IS_ACTIVE]], label %[[CLEANUP_ACTION:.*]], label %[[CLEANUP_DONE:.*]] +// OGCG: [[CLEANUP_ACTION]]: +// OGCG: call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP1]]) +// OGCG: br label %[[CLEANUP_DONE]] +// OGCG: [[CLEANUP_DONE]]: +// OGCG: call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP0]]) + +bool test_temp_and() { return make_temp(1) && make_temp(2); } + +// CIR: cir.func{{.*}} @_Z13test_temp_andv() +// CIR: %[[SCOPE:.*]] = cir.scope { +// CIR: %[[REF_TMP0:.*]] = cir.alloca !rec_B, !cir.ptr, ["ref.tmp0"] +// CIR: %[[ONE:.*]] = cir.const #cir.int<1> +// CIR: cir.call @_ZN1BC2Ei(%[[REF_TMP0]], %[[ONE]]) +// CIR: %[[MAKE_TEMP0:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP0]]) +// CIR: %[[TERNARY:.*]] = cir.ternary(%[[MAKE_TEMP0]], true { +// CIR: %[[REF_TMP1:.*]] = cir.alloca !rec_B, !cir.ptr, ["ref.tmp1"] +// CIR: %[[TWO:.*]] = cir.const #cir.int<2> +// CIR: cir.call @_ZN1BC2Ei(%[[REF_TMP1]], %[[TWO]]) +// CIR: %[[MAKE_TEMP1:.*]] = cir.call @_Z9make_tempRK1B(%[[REF_TMP1]]) +// CIR: cir.call @_ZN1BD2Ev(%[[REF_TMP1]]) +// CIR: cir.yield %[[MAKE_TEMP1]] : !cir.bool +// CIR: }, false { +// CIR: %[[FALSE:.*]] = cir.const #false +// CIR: cir.yield %[[FALSE]] : !cir.bool +// CIR: }) +// CIR: cir.call @_ZN1BD2Ev(%[[REF_TMP0]]) +// CIR: cir.yield %[[TERNARY]] : !cir.bool +// CIR: } : !cir.bool + +// LLVM: define{{.*}} i1 @_Z13test_temp_andv() { +// LLVM: %[[REF_TMP0:.*]] = alloca %struct.B +// LLVM: %[[REF_TMP1:.*]] = alloca %struct.B +// LLVM: br label %[[LAND_BEGIN:.*]] +// LLVM: [[LAND_BEGIN]]: +// LLVM: call void @_ZN1BC2Ei(ptr %[[REF_TMP0]], i32 1) +// LLVM: %[[MAKE_TEMP0:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP0]]) +// LLVM: br i1 %[[MAKE_TEMP0]], label %[[LHS_TRUE_BLOCK:.*]], label %[[LHS_FALSE_BLOCK:.*]] +// LLVM: [[LHS_TRUE_BLOCK]]: +// LLVM: call void @_ZN1BC2Ei(ptr %[[REF_TMP1]], i32 2) +// LLVM: %[[MAKE_TEMP1:.*]] = call i1 @_Z9make_tempRK1B(ptr %[[REF_TMP1]]) +// LLVM: call void @_ZN1BD2Ev(ptr %[[REF_TMP1]]) +// LLVM: br label %[[RESULT_BLOCK:.*]] +// LLVM: [[LHS_FALSE_BLOCK]]: +// LLVM: br label %[[RESULT_BLOCK]] +// LLVM: [[RESULT_BLOCK]]: +// LLVM: %[[RESULT:.*]] = phi i1 [ false, %[[LHS_FALSE_BLOCK]] ], [ %[[MAKE_TEMP1]], %[[LHS_TRUE_BLOCK]] ] +// LLVM: br label %[[LAND_END:.*]] +// LLVM: [[LAND_END]]: +// LLVM: call void @_ZN1BD2Ev(ptr %[[REF_TMP0]]) + +// OGCG: define {{.*}} i1 @_Z13test_temp_andv() +// OGCG: [[ENTRY:.*]]: +// OGCG: %[[RETVAL:.*]] = alloca i1 +// OGCG: %[[REF_TMP0:.*]] = alloca %struct.B +// OGCG: %[[REF_TMP1:.*]] = alloca %struct.B +// OGCG: %[[CLEANUP_COND:.*]] = alloca i1 +// OGCG: call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP0]], i32 {{.*}} 1) +// OGCG: %[[MAKE_TEMP0:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP0]]) +// OGCG: store i1 false, ptr %cleanup.cond +// OGCG: br i1 %[[MAKE_TEMP0]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]] +// OGCG: [[LAND_RHS]]: +// OGCG: call void @_ZN1BC2Ei(ptr {{.*}} %[[REF_TMP1]], i32 {{.*}} 2) +// OGCG: store i1 true, ptr %[[CLEANUP_COND]] +// OGCG: %[[MAKE_TEMP1:.*]] = call {{.*}} i1 @_Z9make_tempRK1B(ptr {{.*}} %[[REF_TMP1]]) +// OGCG: br label %[[LAND_END]] +// OGCG: [[LAND_END]]: +// OGCG: %[[PHI:.*]] = phi i1 [ false, %[[ENTRY]] ], [ %[[MAKE_TEMP1]], %[[LAND_RHS]] ] +// OGCG: store i1 %[[PHI]], ptr %[[RETVAL]] +// OGCG: %[[CLEANUP_IS_ACTIVE:.*]] = load i1, ptr %[[CLEANUP_COND]] +// OGCG: br i1 %[[CLEANUP_IS_ACTIVE]], label %[[CLEANUP_ACTION:.*]], label %[[CLEANUP_DONE:.*]] +// OGCG: [[CLEANUP_ACTION]]: +// OGCG: call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP1]]) +// OGCG: br label %[[CLEANUP_DONE]] +// OGCG: [[CLEANUP_DONE]]: +// OGCG: call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP0]]) From d4aa67661a43d24d95e2123fe624597359d9a109 Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Mon, 6 Oct 2025 14:21:09 -0700 Subject: [PATCH 2/2] Apply review feedback --- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 2a24f72df6800..d1e2c6a13ac0b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -1101,15 +1101,15 @@ class ScalarExprEmitter : public StmtVisitor { cgf.curLexScope->setAsTernary(); mlir::Value res = cgf.evaluateExprAsBool(e->getRHS()); lexScope.forceCleanup(); - b.create(loc, res); + cir::YieldOp::create(b, loc, res); }, /*falseBuilder*/ [&](mlir::OpBuilder &b, mlir::Location loc) { CIRGenFunction::LexicalScope lexScope{cgf, loc, b.getInsertionBlock()}; cgf.curLexScope->setAsTernary(); - auto res = b.create(loc, builder.getFalseAttr()); - b.create(loc, res.getRes()); + auto res = cir::ConstantOp::create(b, loc, builder.getFalseAttr()); + cir::YieldOp::create(b, loc, res.getRes()); }); return maybePromoteBoolResult(resOp.getResult(), resTy); } @@ -1145,8 +1145,8 @@ class ScalarExprEmitter : public StmtVisitor { CIRGenFunction::LexicalScope lexScope{cgf, loc, b.getInsertionBlock()}; cgf.curLexScope->setAsTernary(); - auto res = b.create(loc, builder.getTrueAttr()); - b.create(loc, res.getRes()); + auto res = cir::ConstantOp::create(b, loc, builder.getTrueAttr()); + cir::YieldOp::create(b, loc, res.getRes()); }, /*falseBuilder*/ [&](mlir::OpBuilder &b, mlir::Location loc) { @@ -1155,7 +1155,7 @@ class ScalarExprEmitter : public StmtVisitor { cgf.curLexScope->setAsTernary(); mlir::Value res = cgf.evaluateExprAsBool(e->getRHS()); lexScope.forceCleanup(); - b.create(loc, res); + cir::YieldOp::create(b, loc, res); }); return maybePromoteBoolResult(resOp.getResult(), resTy);