[clang] [CIR] Add support for globals reference variables (PR #182608)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 20 14:40:04 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Erich Keane (erichkeane)
<details>
<summary>Changes</summary>
These are fairly simple, particularly if they don't need special cleanups (which is left unimplemented), but this provides init for a global reference variable.
---
Full diff: https://github.com/llvm/llvm-project/pull/182608.diff
8 Files Affected:
- (modified) clang/include/clang/CIR/MissingFeatures.h (+1)
- (modified) clang/lib/CIR/CodeGen/CIRGenCXX.cpp (+30-1)
- (modified) clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h (+2)
- (modified) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+7-4)
- (modified) clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp (+13-3)
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+108)
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+5)
- (added) clang/test/CIR/CodeGenCXX/global-refs.cpp (+130)
``````````diff
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 97c76df0bb0b9..4b26dd96fc3ee 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -279,6 +279,7 @@ struct MissingFeatures {
static bool emitNullabilityCheck() { return false; }
static bool emitTypeCheck() { return false; }
static bool emitTypeMetadataCodeForVCall() { return false; }
+ static bool materializedGlobalTempCache() { return false; }
// Fast math.
static bool fastMathGuard() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
index c3457e40a9110..3033f1c810845 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
@@ -294,5 +294,34 @@ void CIRGenModule::emitCXXGlobalVarDeclInit(const VarDecl *varDecl,
return;
}
- errorNYI(varDecl->getSourceRange(), "global with reference type");
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ auto *block = builder.createBlock(&addr.getCtorRegion());
+ CIRGenFunction::LexicalScope scope{*curCGF, addr.getLoc(),
+ builder.getInsertionBlock()};
+ scope.setAsGlobalInit();
+ builder.setInsertionPointToStart(block);
+ mlir::Value getGlobal = builder.createGetGlobal(addr);
+
+ Address declAddr(getGlobal, getASTContext().getDeclAlign(varDecl));
+ assert(performInit && "cannot have a constant initializer which needs "
+ "destruction for reference");
+ RValue rv = cgf.emitReferenceBindingToExpr(varDecl->getInit());
+ {
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ mlir::Operation *rvalDefOp = rv.getValue().getDefiningOp();
+ if (rvalDefOp && rvalDefOp->getBlock()) {
+ mlir::Block *rvalSrcBlock = rvalDefOp->getBlock();
+
+ if (!rvalSrcBlock->empty() && isa<cir::YieldOp>(rvalSrcBlock->back())) {
+ mlir::Operation &front = rvalSrcBlock->front();
+ getGlobal.getDefiningOp()->moveBefore(&front);
+ builder.setInsertionPoint(cast<cir::YieldOp>(rvalSrcBlock->back()));
+ }
+ }
+ cgf.emitStoreOfScalar(rv.getValue(), declAddr, /*isVolatile=*/false,
+ ty, LValueBaseInfo{});
+ }
+
+ builder.setInsertionPointToEnd(block);
+ cir::YieldOp::create(builder, addr->getLoc());
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
index 1cd7b5bffb1dc..5280198524773 100644
--- a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
+++ b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
@@ -65,6 +65,8 @@ class ConstantEmitter {
/// constant. If this succeeds, the emission must be finalized.
mlir::Attribute tryEmitForInitializer(const VarDecl &d);
+ mlir::Attribute emitForInitializer(const APValue &value, QualType destType);
+
void finalize(cir::GlobalOp gv);
// All of the "abstract" emission methods below permit the emission to
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 83d51bac01d1e..69d98c34c249c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1657,10 +1657,13 @@ static Address createReferenceTemporary(CIRGenFunction &cgf,
}
case SD_Thread:
case SD_Static: {
- cgf.cgm.errorNYI(
- m->getSourceRange(),
- "createReferenceTemporary: static/thread storage duration");
- return Address::invalid();
+ auto addr =
+ mlir::cast<cir::GlobalOp>(cgf.cgm.getAddrOfGlobalTemporary(m, inner));
+ auto getGlobal = cgf.cgm.getBuilder().createGetGlobal(addr);
+ assert(addr.getAlignment().has_value() &&
+ "This should always have an alignment");
+ return Address(getGlobal,
+ clang::CharUnits::fromQuantity(addr.getAlignment().value()));
}
case SD_Dynamic:
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 462e73b3acc8d..e3dca9bc0f3c7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -1485,9 +1485,11 @@ ConstantLValueEmitter::VisitCXXTypeidExpr(const CXXTypeidExpr *e) {
ConstantLValue ConstantLValueEmitter::VisitMaterializeTemporaryExpr(
const MaterializeTemporaryExpr *e) {
- cgm.errorNYI(e->getSourceRange(),
- "ConstantLValueEmitter: materialize temporary expr");
- return {};
+ assert(e->getStorageDuration() == SD_Static);
+ const Expr *inner = e->getSubExpr()->skipRValueSubobjectAdjustments();
+ mlir::Operation *global = cgm.getAddrOfGlobalTemporary(e, inner);
+ return ConstantLValue(
+ cgm.getBuilder().getGlobalViewAttr(mlir::cast<cir::GlobalOp>(global)));
}
//===----------------------------------------------------------------------===//
@@ -1499,6 +1501,14 @@ mlir::Attribute ConstantEmitter::tryEmitForInitializer(const VarDecl &d) {
return markIfFailed(tryEmitPrivateForVarInit(d));
}
+mlir::Attribute ConstantEmitter::emitForInitializer(const APValue &value,
+ QualType destType) {
+ initializeNonAbstract();
+ auto c = tryEmitPrivateForMemory(value, destType);
+ assert(c && "couldn't emit constant value non-abstractly?");
+ return c;
+}
+
void ConstantEmitter::finalize(cir::GlobalOp gv) {
assert(initializedNonAbstract &&
"finalizing emitter that was used for abstract emission?");
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index dbd3c92797f23..8c8483bae94d9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2884,3 +2884,111 @@ cir::LabelOp
CIRGenModule::lookupBlockAddressInfo(cir::BlockAddrInfoAttr blockInfo) {
return blockAddressInfoToLabel.lookup(blockInfo);
}
+
+mlir::Operation *
+CIRGenModule::getAddrOfGlobalTemporary(const MaterializeTemporaryExpr *mte,
+ const Expr *init) {
+ assert((mte->getStorageDuration() == SD_Static ||
+ mte->getStorageDuration() == SD_Thread) &&
+ "not a global temporary");
+ const auto *varDecl = cast<VarDecl>(mte->getExtendingDecl());
+
+ // If we're not materializing a subobject of the temporary, keep the
+ // cv-qualifiers from the type of the MaterializeTemporaryExpr.
+ QualType materializedType = init->getType();
+ if (init == mte->getSubExpr())
+ materializedType = mte->getType();
+
+ CharUnits align = getASTContext().getTypeAlignInChars(materializedType);
+
+ assert(!cir::MissingFeatures::materializedGlobalTempCache());
+
+ // FIXME: If an externally-visible declaration extends multiple temporaries,
+ // we need to give each temporary the same name in every translation unit (and
+ // we also need to make the temporaries externally-visible).
+ llvm::SmallString<256> name;
+ llvm::raw_svector_ostream out(name);
+ getCXXABI().getMangleContext().mangleReferenceTemporary(
+ varDecl, mte->getManglingNumber(), out);
+
+ APValue *value = nullptr;
+ if (mte->getStorageDuration() == SD_Static && varDecl->evaluateValue()) {
+ // If the initializer of the extending declaration is a constant
+ // initializer, we should have a cached constant initializer for this
+ // temporay. Note taht this m ight have a different value from the value
+ // computed by evaluating the initializer if the surrounding constant
+ // expression modifies the temporary.
+ value = mte->getOrCreateValue(/*MayCreate=*/false);
+ }
+
+ // Try evaluating it now, it might have a constant initializer
+ Expr::EvalResult evalResult;
+ if (!value && init->EvaluateAsRValue(evalResult, getASTContext()) &&
+ !evalResult.hasSideEffects())
+ value = &evalResult.Val;
+
+ assert(!cir::MissingFeatures::addressSpace());
+
+ std::optional<ConstantEmitter> emitter;
+ mlir::Attribute initialValue = nullptr;
+ bool isConstant = false;
+ mlir::Type type;
+
+ if (value) {
+ emitter.emplace(*this);
+ initialValue =
+ emitter->emitForInitializer(*value, materializedType);
+
+ isConstant = materializedType.isConstantStorage(
+ getASTContext(), /*ExcludeCtor=*/value, /*ExcludeDtor=*/false);
+
+ type = mlir::cast<mlir::TypedAttr>(initialValue).getType();
+ } else {
+ // No initializer, the initialization will be provided when we initialize
+ // the declaration which performed lifetime extension.
+ type = getTypes().convertTypeForMem(materializedType);
+ }
+
+ // Create a global variable for this lifetime-extended temporary.
+ cir::GlobalLinkageKind linkage =
+ getCIRLinkageVarDefinition(varDecl, /*isConstant=*/false);
+ if (linkage == cir::GlobalLinkageKind::ExternalLinkage) {
+ const VarDecl *initVD;
+ if (varDecl->isStaticDataMember() && varDecl->getAnyInitializer(initVD) &&
+ isa<CXXRecordDecl>(initVD->getLexicalDeclContext())) {
+ // Temporaries defined inside a class get linkonce_odr linkage because the
+ // calss can be defined in multiple translation units.
+ errorNYI(mte->getSourceRange(), "static data member initialization");
+ } else {
+ // There is no need for this temporary to have external linkage if the
+ // VarDecl has external linkage.
+ linkage = cir::GlobalLinkageKind::InternalLinkage;
+ }
+ }
+ auto loc = getLoc(mte->getSourceRange());
+ auto gv = createGlobalOp(*this, loc, name, type, isConstant);
+ gv.setInitialValueAttr(initialValue);
+
+ if (emitter)
+ emitter->finalize(gv);
+ // Don't assign dllimport or dllexport to local linkage globals
+ if (!gv.hasLocalLinkage()) {
+ setGVProperties(gv, varDecl);
+ assert(!cir::MissingFeatures::setDLLStorageClass());
+ }
+
+ gv.setAlignment(align.getAsAlign().value());
+ if (supportsCOMDAT() && gv.isWeakForLinker())
+ errorNYI(mte->getSourceRange(),
+ "Global temporary with comdat/weak linkage");
+ if (varDecl->getTLSKind())
+ errorNYI(mte->getSourceRange(),
+ "Global temporary with thread local storage");
+ mlir::Operation *cv = gv;
+
+ assert(!cir::MissingFeatures::addressSpace());
+
+ assert(!cir::MissingFeatures::materializedGlobalTempCache());
+
+ return cv;
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 5b8a105e4912f..3f5a8490864b3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -655,6 +655,11 @@ class CIRGenModule : public CIRGenTypeCache {
// Finalize CIR code generation.
void release();
+ /// Returns a pointer to a global variable representing a temporary with
+ /// static or thread storage duration.
+ mlir::Operation *getAddrOfGlobalTemporary(const MaterializeTemporaryExpr *mte,
+ const Expr *init);
+
/// -------
/// Visibility and Linkage
/// -------
diff --git a/clang/test/CIR/CodeGenCXX/global-refs.cpp b/clang/test/CIR/CodeGenCXX/global-refs.cpp
new file mode 100644
index 0000000000000..4271b7ecf3e6b
--- /dev/null
+++ b/clang/test/CIR/CodeGenCXX/global-refs.cpp
@@ -0,0 +1,130 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s --check-prefixes=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefixes=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefixes=LLVM
+
+struct DefCtor{};
+struct WithCtor{
+ WithCtor();
+ WithCtor(int);
+};
+
+struct WithCtorDtor{
+ WithCtorDtor();
+ WithCtorDtor(int);
+ ~WithCtorDtor();
+};
+
+int globalInt;
+// CIR: cir.global external @globalInt = #cir.int<0> : !s32i {alignment = 4 : i64}
+// LLVM: @globalInt = global i32 0, align 4
+
+int &globalIntRef = globalInt;
+// CIR: cir.global constant external @globalIntRef = #cir.global_view<@globalInt> : !cir.ptr<!s32i> {alignment = 8 : i64}
+// LLVM: @globalIntRef = constant ptr @globalInt, align 8
+
+const int &constGlobalIntRef = 5;
+// CIR: cir.global "private" external @_ZGR17constGlobalIntRef_ = #cir.int<5> : !s32i {alignment = 4 : i64}
+// CIR: cir.global constant external @constGlobalIntRef = #cir.global_view<@_ZGR17constGlobalIntRef_> : !cir.ptr<!s32i> {alignment = 8 : i64}
+// LLVM: @_ZGR17constGlobalIntRef_ = {{.*}}global i32 5, align 4
+// LLVM: @constGlobalIntRef = constant ptr @_ZGR17constGlobalIntRef_, align 8
+
+DefCtor defCtor{};
+// CIR: cir.global external @defCtor = #cir.undef : !rec_DefCtor {alignment = 1 : i64}
+// LLVM: @defCtor = global %struct.DefCtor undef, align 1
+// OGCG: FAIL
+
+DefCtor &defCtorRef = defCtor;
+// CIR: cir.global constant external @defCtorRef = #cir.global_view<@defCtor> : !cir.ptr<!rec_DefCtor> {alignment = 8 : i64}
+// LLVM: @defCtorRef = constant ptr @defCtor, align 8
+// OGCG: FAIL
+
+const DefCtor &constDefCtorRef{};
+// CIR: cir.global "private" constant external @_ZGR15constDefCtorRef_ = #cir.undef : !rec_DefCtor {alignment = 1 : i64}
+// CIR: cir.global constant external @constDefCtorRef = #cir.global_view<@_ZGR15constDefCtorRef_> : !cir.ptr<!rec_DefCtor> {alignment = 8 : i64}
+// LLVM: @_ZGR15constDefCtorRef_ = {{.*}}constant %struct.DefCtor undef, align 1
+// LLVM: @constDefCtorRef = constant ptr @_ZGR15constDefCtorRef_, align 8
+// OGCG: FAIL
+
+WithCtor withCtor{};
+// CIR: cir.global external @withCtor = #cir.zero : !rec_WithCtor {alignment = 1 : i64, ast = #cir.var.decl.ast}
+// CIR-NEXT: cir.func internal private @__cxx_global_var_init{{.*}}() {
+// CIR-NEXT: %[[GET_GLOB:.*]] = cir.get_global @withCtor : !cir.ptr<!rec_WithCtor>
+// CIR-NEXT: cir.call @_ZN8WithCtorC1Ev(%[[GET_GLOB]]) : (!cir.ptr<!rec_WithCtor>{{.*}}) -> ()
+// CIR-NEXT: cir.return
+// CIR-NEXT: }
+// LLVM: @withCtor = global %struct.WithCtor zeroinitializer, align 1
+// OGCG: FAIL
+
+WithCtor &withCtorRef = withCtor;
+// CIR: cir.global constant external @withCtorRef = #cir.global_view<@withCtor> : !cir.ptr<!rec_WithCtor> {alignment = 8 : i64}
+// LLVM: @withCtorRef = constant ptr @withCtor, align 8
+// OGCG: FAIL
+
+const WithCtor &constWithCtorRef{};
+// CIR: cir.global external @constWithCtorRef = #cir.ptr<null> : !cir.ptr<!rec_WithCtor> {alignment = 8 : i64, ast = #cir.var.decl.ast}
+// CIR-NEXT: cir.func internal private @__cxx_global_var_init{{.*}}() {
+// CIR-NEXT: %[[GET_GLOB:.*]] = cir.get_global @constWithCtorRef : !cir.ptr<!cir.ptr<!rec_WithCtor>>
+// CIR-NEXT: %[[GET_GLOB_OBJ:.*]] = cir.get_global @_ZGR16constWithCtorRef_ : !cir.ptr<!rec_WithCtor>
+// CIR-NEXT: cir.call @_ZN8WithCtorC1Ev(%[[GET_GLOB_OBJ]]) : (!cir.ptr<!rec_WithCtor>{{.*}}) -> ()
+// CIR-NEXT: cir.store align(8) %[[GET_GLOB_OBJ]], %[[GET_GLOB]] : !cir.ptr<!rec_WithCtor>, !cir.ptr<!cir.ptr<!rec_WithCtor>>
+// CIR-NEXT: cir.return
+// CIR-NEXT: }
+// LLVM: @constWithCtorRef = global ptr null, align 8
+// OGCG: FAIL
+
+const WithCtor &constWithCtorRef2{5};
+// CIR: cir.global external @constWithCtorRef2 = #cir.ptr<null> : !cir.ptr<!rec_WithCtor> {alignment = 8 : i64, ast = #cir.var.decl.ast}
+// CIR-NEXT: cir.func internal private @__cxx_global_var_init{{.*}}() {
+// CIR-NEXT: %[[GET_GLOB:.*]] = cir.get_global @constWithCtorRef2 : !cir.ptr<!cir.ptr<!rec_WithCtor>>
+// CIR-NEXT: %[[GET_GLOB_OBJ:.*]] = cir.get_global @_ZGR17constWithCtorRef2_ : !cir.ptr<!rec_WithCtor>
+// CIR-NEXT: %[[FIVE:.*]] = cir.const #cir.int<5> : !s32i
+// CIR-NEXT: cir.call @_ZN8WithCtorC1Ei(%[[GET_GLOB_OBJ]], %[[FIVE]]) : (!cir.ptr<!rec_WithCtor>{{.*}}, !s32i{{.*}}) -> ()
+// CIR-NEXT: cir.store align(8) %[[GET_GLOB_OBJ]], %[[GET_GLOB]] : !cir.ptr<!rec_WithCtor>, !cir.ptr<!cir.ptr<!rec_WithCtor>>
+// CIR-NEXT: cir.return
+// CIR-NEXT: }
+// LLVM: @constWithCtorRef2 = global ptr null, align 8
+// OGCG: FAIL
+
+WithCtorDtor withCtorDtor{};
+// CIR: cir.global external @withCtorDtor = #cir.zero : !rec_WithCtorDtor {alignment = 1 : i64, ast = #cir.var.decl.ast}
+// CIR: cir.func internal private @__cxx_global_var_init{{.*}}() {
+// CIR-NEXT: %[[GET_GLOB:.*]] = cir.get_global @withCtorDtor : !cir.ptr<!rec_WithCtorDtor>
+// CIR-NEXT: cir.call @_ZN12WithCtorDtorC1Ev(%[[GET_GLOB]]) : (!cir.ptr<!rec_WithCtorDtor>{{.*}}) -> ()
+// CIR-NEXT: %[[GET_GLOB:.*]] = cir.get_global @withCtorDtor : !cir.ptr<!rec_WithCtorDtor>
+// CIR-NEXT: %[[GET_DTOR:.*]] = cir.get_global @_ZN12WithCtorDtorD1Ev : !cir.ptr<!cir.func<(!cir.ptr<!rec_WithCtorDtor>)>>
+// CIR-NEXT: %[[VOID_FN_PTR:.*]] = cir.cast bitcast %[[GET_DTOR]] : !cir.ptr<!cir.func<(!cir.ptr<!rec_WithCtorDtor>)>> -> !cir.ptr<!cir.func<(!cir.ptr<!void>)>>
+// CIR-NEXT: %[[GLOB_TO_VOID:.*]] = cir.cast bitcast %[[GET_GLOB]] : !cir.ptr<!rec_WithCtorDtor> -> !cir.ptr<!void>
+// CIR-NEXT: %[[DSO_HANDLE:.*]] = cir.get_global @__dso_handle : !cir.ptr<i8>
+// CIR-NEXT: cir.call @__cxa_atexit(%[[VOID_FN_PTR]], %[[GLOB_TO_VOID]], %[[DSO_HANDLE]]) : (!cir.ptr<!cir.func<(!cir.ptr<!void>)>>, !cir.ptr<!void>, !cir.ptr<i8>{{.*}}) -> ()
+// CIR-NEXT: cir.return
+// CIR-NEXT: }
+// LLVM: @withCtorDtor = global %struct.WithCtorDtor zeroinitializer, align 1
+// OGCG: FAIL
+
+WithCtorDtor &withCtorDtorRef = withCtorDtor;
+// CIR: cir.global constant external @withCtorDtorRef = #cir.global_view<@withCtorDtor> : !cir.ptr<!rec_WithCtorDtor> {alignment = 8 : i64}
+// LLVM: @withCtorDtorRef = constant ptr @withCtorDtor, align 8
+// OGCG: FAIL
+
+// LLVM: define internal void @__cxx_global_var_init{{.*}}()
+// LLVM: call void @_ZN8WithCtorC1Ev(ptr {{.*}}@withCtor)
+// LLVM-NEXT: ret void
+
+// LLVM: define internal void @__cxx_global_var_init{{.*}}()
+// LLVM: call void @_ZN8WithCtorC1Ev(ptr {{.*}}@_ZGR16constWithCtorRef_)
+// LLVM-NEXT: store ptr @_ZGR16constWithCtorRef_, ptr @constWithCtorRef, align 8
+// LLVM-NEXT: ret void
+
+// LLVM: define internal void @__cxx_global_var_init{{.*}}()
+// LLVM: call void @_ZN8WithCtorC1Ei(ptr {{.*}}@_ZGR17constWithCtorRef2_, i32 {{.*}}5)
+// LLVM-NEXT: store ptr @_ZGR17constWithCtorRef2_, ptr @constWithCtorRef2, align 8
+// LLVM-NEXT: ret void
+
+// LLVM: define internal void @__cxx_global_var_init{{.*}}()
+// LLVM: call void @_ZN12WithCtorDtorC1Ev(ptr {{.*}}@withCtorDtor)
+// LLVM-NEXT: call {{.*}}@__cxa_atexit(ptr {{.*}}@_ZN12WithCtorDtorD1Ev, ptr {{.*}}@withCtorDtor, ptr {{.*}}@__dso_handle)
+// LLVM-NEXT: ret void
+
+// TODO(cir): Once we get destructors for temporaries done, we should test them
+// here, same as the 'const-WithCtor' examples, except with the 'withCtorDtor'
+// versions.
``````````
</details>
https://github.com/llvm/llvm-project/pull/182608
More information about the cfe-commits
mailing list