https://github.com/adams381 updated https://github.com/llvm/llvm-project/pull/188300
>From 4bf773038476e7ce82493ee6ba99e6e134401e22 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Wed, 8 Apr 2026 15:01:09 -0700 Subject: [PATCH 1/2] [CIR][ABI] Move record ABI metadata to module-level attribute Move canPassInRegisters, hasTrivialDestructor, and recordAlignInBytes out of RecordTypeStorage and into a module-level cir.record_layouts dictionary attribute. These are translation-unit / target properties, not intrinsic to the type. Introduce ArgPassingKind enum with three states (CanPassInRegs, CannotPassInRegs, CanNeverPassInRegs) mirroring the AST's RecordArgPassingKind, and RecordLayoutAttr to hold the metadata. Entries are accumulated during CIRGen and materialized once in CIRGenModule::release(). Add getRecordLayout(ModuleOp, StringAttr) helper for lookup. Addresses review feedback from @xlauko, @koparasy, @andykaylor, and @erichkeane on PR #188300. Made-with: Cursor --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 62 +++++++++++++ .../include/clang/CIR/Dialect/IR/CIRDialect.h | 4 + .../clang/CIR/Dialect/IR/CIRDialect.td | 1 + clang/lib/CIR/CodeGen/CIRGenModule.cpp | 5 + clang/lib/CIR/CodeGen/CIRGenModule.h | 8 ++ .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 27 ++++++ clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 14 +++ clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 15 ++- .../test/CIR/CodeGen/record-type-metadata.cpp | 29 ++++++ clang/unittests/CIR/CMakeLists.txt | 1 + .../unittests/CIR/RecordTypeMetadataTest.cpp | 91 +++++++++++++++++++ .../gn/secondary/clang/unittests/CIR/BUILD.gn | 5 +- 12 files changed, 253 insertions(+), 9 deletions(-) create mode 100644 clang/test/CIR/CodeGen/record-type-metadata.cpp create mode 100644 clang/unittests/CIR/RecordTypeMetadataTest.cpp diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 01bac73e441a8..c6f95c301cdad 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -110,6 +110,68 @@ def CIR_SourceLanguageAttr : CIR_EnumAttr<CIR_SourceLanguage, "lang"> { }]; } +//===----------------------------------------------------------------------===// +// ArgPassingKind + RecordLayoutAttr +//===----------------------------------------------------------------------===// + +def CIR_ArgPassingKind : CIR_I32EnumAttr< + "ArgPassingKind", "record argument passing eligibility", [ + I32EnumAttrCase<"CanPassInRegs", 0, "can_pass_in_regs">, + I32EnumAttrCase<"CannotPassInRegs", 1, "cannot_pass_in_regs">, + I32EnumAttrCase<"CanNeverPassInRegs", 2, "can_never_pass_in_regs"> +]> { + let genSpecializedAttr = 0; +} + +def CIR_RecordLayoutAttr : CIR_Attr<"RecordLayout", "record_layout"> { + let summary = "ABI layout metadata for a record type"; + let description = [{ + Holds AST-derived ABI metadata for a named record type. These + properties are translation-unit / target properties, not intrinsic + to the type, so they live on the module rather than on RecordType. + + Fields: + - `arg_passing_kind`: whether the record can be passed in registers + per the C++ ABI (mirrors `RecordDecl::getArgPassingRestrictions()`). + - `has_trivial_destructor`: from `CXXRecordDecl::hasTrivialDestructor()`. + - `record_align_in_bytes`: from `ASTRecordLayout::getAlignment()`. + Needed because CIR's DataLayout cannot account for + `__attribute__((aligned(N)))`. + + Example: + ``` + module attributes { + cir.record_layouts = { + "Trivial" = #cir.record_layout< + arg_passing_kind = can_pass_in_regs, + has_trivial_dtor = true, + record_align = 4>, + "NonTrivialDtor" = #cir.record_layout< + arg_passing_kind = cannot_pass_in_regs, + has_trivial_dtor = false, + record_align = 4> + } + } + ``` + }]; + + let parameters = (ins + EnumParameter<CIR_ArgPassingKind>:$arg_passing_kind, + "bool":$has_trivial_dtor, + "uint64_t":$record_align + ); + + let assemblyFormat = [{ + `<` + `arg_passing_kind` `=` $arg_passing_kind `,` + `has_trivial_dtor` `=` $has_trivial_dtor `,` + `record_align` `=` $record_align + `>` + }]; + + let canHaveIllegalCXXABIType = 0; +} + //===----------------------------------------------------------------------===// // OptInfoAttr //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h index ddcb988f9ea84..970a6984a5b05 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h @@ -41,6 +41,10 @@ using BuilderOpStateCallbackRef = llvm::function_ref<void( namespace cir { void buildTerminatedBody(mlir::OpBuilder &builder, mlir::Location loc); + +/// Look up the RecordLayoutAttr for a named record in the module's +/// cir.record_layouts dictionary. Asserts if the entry is missing. +RecordLayoutAttr getRecordLayout(mlir::ModuleOp module, mlir::StringAttr name); } // namespace cir // TableGen'erated files for MLIR dialects require that a macro be defined when diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td index f1f94c868e5b0..13095464a3fd2 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td @@ -75,6 +75,7 @@ def CIR_Dialect : Dialect { static llvm::StringRef getDefaultFuncAttrsAttrName() { return "default_func_attrs"; } static llvm::StringRef getResAttrsAttrName() { return "res_attrs"; } static llvm::StringRef getArgAttrsAttrName() { return "arg_attrs"; } + static llvm::StringRef getRecordLayoutsAttrName() { return "cir.record_layouts"; } static llvm::StringRef getAMDGPUCodeObjectVersionAttrName() { return "cir.amdhsa_code_object_version"; } static llvm::StringRef getAMDGPUPrintfKindAttrName() { return "cir.amdgpu_printf_kind"; } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 2037b92e2b5d1..46ec883a9f57d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -3102,6 +3102,11 @@ void CIRGenModule::release() { theModule->setAttr(cir::CIRDialect::getModuleLevelAsmAttrName(), builder.getArrayAttr(globalScopeAsm)); + if (!recordLayoutEntries.empty()) + theModule->setAttr( + cir::CIRDialect::getRecordLayoutsAttrName(), + mlir::DictionaryAttr::get(&getMLIRContext(), recordLayoutEntries)); + if (getTriple().isAMDGPU() || (getTriple().isSPIRV() && getTriple().getVendor() == llvm::Triple::AMD)) emitAMDGPUMetadata(); diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 266510de84fd0..addfa1d6c0424 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -101,6 +101,9 @@ class CIRGenModule : public CIRGenTypeCache { llvm::SmallVector<mlir::Attribute> globalScopeAsm; + /// Accumulated record layout entries, materialized in release(). + llvm::SmallVector<mlir::NamedAttribute> recordLayoutEntries; + llvm::DenseSet<clang::GlobalDecl> diagnosedConflictingDefinitions; /// A queue of (optional) vtables to consider emitting. @@ -132,6 +135,11 @@ class CIRGenModule : public CIRGenTypeCache { public: mlir::ModuleOp getModule() const { return theModule; } CIRGenBuilderTy &getBuilder() { return builder; } + + /// Queue a record layout entry for materialization in release(). + void addRecordLayout(mlir::StringAttr name, cir::RecordLayoutAttr attr) { + recordLayoutEntries.push_back(mlir::NamedAttribute(name, attr)); + } clang::ASTContext &getASTContext() const { return astContext; } const clang::TargetInfo &getTarget() const { return target; } const clang::CodeGenOptions &getCodeGenOpts() const { return codeGenOpts; } diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index 7e1f9c6768e61..f76f21ce60814 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -700,6 +700,33 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) { assert(!cir::MissingFeatures::astRecordDeclAttr()); ty->complete(lowering.fieldTypes, lowering.packed, lowering.padded); + // Queue ABI metadata for the module-level cir.record_layouts attribute. + if (ty->getName()) { + mlir::MLIRContext *mlirCtx = ty->getContext(); + auto apk = cir::ArgPassingKind::CannotPassInRegs; + switch (rd->getArgPassingRestrictions()) { + case RecordArgPassingKind::CanPassInRegs: + apk = cir::ArgPassingKind::CanPassInRegs; + break; + case RecordArgPassingKind::CannotPassInRegs: + apk = cir::ArgPassingKind::CannotPassInRegs; + break; + case RecordArgPassingKind::CanNeverPassInRegs: + apk = cir::ArgPassingKind::CanNeverPassInRegs; + break; + } + + bool hasTrivialDestructor = true; + if (auto *cxxRD = dyn_cast<CXXRecordDecl>(rd)) + hasTrivialDestructor = cxxRD->hasTrivialDestructor(); + const auto &astLayout = astContext.getASTRecordLayout(rd); + uint64_t recordAlignInBytes = astLayout.getAlignment().getQuantity(); + + cgm.addRecordLayout(ty->getName(), cir::RecordLayoutAttr::get( + mlirCtx, apk, hasTrivialDestructor, + recordAlignInBytes)); + } + auto rl = std::make_unique<CIRGenRecordLayout>( ty ? *ty : cir::RecordType{}, baseTy ? baseTy : cir::RecordType{}, (bool)lowering.zeroInitializable, (bool)lowering.zeroInitializableAsBase); diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index 27cba6a20b445..270c55dfc4541 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -764,6 +764,20 @@ LogicalResult DynamicCastInfoAttr::verify( return success(); } +//===----------------------------------------------------------------------===// +// RecordLayout lookup +//===----------------------------------------------------------------------===// + +RecordLayoutAttr cir::getRecordLayout(mlir::ModuleOp module, + mlir::StringAttr name) { + auto dict = module->getAttrOfType<mlir::DictionaryAttr>( + CIRDialect::getRecordLayoutsAttrName()); + assert(dict && "module missing cir.record_layouts attribute"); + auto attr = dict.getAs<RecordLayoutAttr>(name); + assert(attr && "record layout entry missing for named record"); + return attr; +} + //===----------------------------------------------------------------------===// // CIR Dialect //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp index 466064d783f2c..9f2342254a882 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -336,14 +336,13 @@ Type RecordType::getLargestMember(const ::mlir::DataLayout &dataLayout) const { auto endIt = getPadded() ? std::prev(members.end()) : members.end(); if (endIt == members.begin()) return {}; - return *std::max_element( - members.begin(), endIt, [&](Type lhs, Type rhs) { - return dataLayout.getTypeABIAlignment(lhs) < - dataLayout.getTypeABIAlignment(rhs) || - (dataLayout.getTypeABIAlignment(lhs) == - dataLayout.getTypeABIAlignment(rhs) && - dataLayout.getTypeSize(lhs) < dataLayout.getTypeSize(rhs)); - }); + return *std::max_element(members.begin(), endIt, [&](Type lhs, Type rhs) { + return dataLayout.getTypeABIAlignment(lhs) < + dataLayout.getTypeABIAlignment(rhs) || + (dataLayout.getTypeABIAlignment(lhs) == + dataLayout.getTypeABIAlignment(rhs) && + dataLayout.getTypeSize(lhs) < dataLayout.getTypeSize(rhs)); + }); } bool RecordType::isLayoutIdentical(const RecordType &other) { diff --git a/clang/test/CIR/CodeGen/record-type-metadata.cpp b/clang/test/CIR/CodeGen/record-type-metadata.cpp new file mode 100644 index 0000000000000..39d7d128618ea --- /dev/null +++ b/clang/test/CIR/CodeGen/record-type-metadata.cpp @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s + +struct Trivial { int x, y; }; +struct Empty {}; +struct __attribute__((aligned(16))) Aligned { int a, b; }; + +class NonTrivialDtor { + int val; +public: + ~NonTrivialDtor(); +}; + +void takesTrivial(Trivial t) {} +void takesEmpty(Empty e) {} +void takesAligned(Aligned a) {} +void takesNTD(NonTrivialDtor n) {} + +// Record types should NOT contain ABI metadata keywords. +// CIR-DAG: !rec_Trivial = !cir.record<struct "Trivial" {!s32i, !s32i}> +// CIR-DAG: !rec_Empty = !cir.record<struct "Empty" padded {!u8i}> +// CIR-DAG: !rec_Aligned = !cir.record<struct "Aligned" padded {!s32i, !s32i, !cir.array<!u8i x 8>}> +// CIR-DAG: !rec_NonTrivialDtor = !cir.record<class "NonTrivialDtor" {!s32i}> + +// ABI metadata lives in module-level cir.record_layouts attribute. +// CIR-DAG: Trivial = #cir.record_layout<arg_passing_kind = can_pass_in_regs, has_trivial_dtor = true, record_align = 4> +// CIR-DAG: Empty = #cir.record_layout<arg_passing_kind = can_pass_in_regs, has_trivial_dtor = true, record_align = 1> +// CIR-DAG: Aligned = #cir.record_layout<arg_passing_kind = can_pass_in_regs, has_trivial_dtor = true, record_align = 16> +// CIR-DAG: NonTrivialDtor = #cir.record_layout<arg_passing_kind = cannot_pass_in_regs, has_trivial_dtor = false, record_align = 4> diff --git a/clang/unittests/CIR/CMakeLists.txt b/clang/unittests/CIR/CMakeLists.txt index 650fde38c48a9..0514dd961b7b9 100644 --- a/clang/unittests/CIR/CMakeLists.txt +++ b/clang/unittests/CIR/CMakeLists.txt @@ -5,6 +5,7 @@ include_directories(${MLIR_TABLEGEN_OUTPUT_DIR}) add_distinct_clang_unittest(CIRUnitTests PointerLikeTest.cpp + RecordTypeMetadataTest.cpp LLVM_COMPONENTS Core diff --git a/clang/unittests/CIR/RecordTypeMetadataTest.cpp b/clang/unittests/CIR/RecordTypeMetadataTest.cpp new file mode 100644 index 0000000000000..79d278d9de3a1 --- /dev/null +++ b/clang/unittests/CIR/RecordTypeMetadataTest.cpp @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Unit tests for CIR RecordLayoutAttr (module-level ABI metadata). +// +//===----------------------------------------------------------------------===// + +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/MLIRContext.h" +#include "clang/CIR/Dialect/IR/CIRAttrs.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "gtest/gtest.h" + +using namespace mlir; +using namespace cir; + +class RecordLayoutAttrTest : public ::testing::Test { +protected: + RecordLayoutAttrTest() { context.loadDialect<cir::CIRDialect>(); } + + MLIRContext context; + + mlir::StringAttr getName(llvm::StringRef name) { + return mlir::StringAttr::get(&context, name); + } +}; + +TEST_F(RecordLayoutAttrTest, CanPassInRegs) { + auto attr = + RecordLayoutAttr::get(&context, ArgPassingKind::CanPassInRegs, true, 4); + EXPECT_EQ(attr.getArgPassingKind(), ArgPassingKind::CanPassInRegs); + EXPECT_TRUE(attr.getHasTrivialDtor()); + EXPECT_EQ(attr.getRecordAlign(), 4u); +} + +TEST_F(RecordLayoutAttrTest, CannotPassInRegs) { + auto attr = RecordLayoutAttr::get(&context, ArgPassingKind::CannotPassInRegs, + false, 4); + EXPECT_EQ(attr.getArgPassingKind(), ArgPassingKind::CannotPassInRegs); + EXPECT_FALSE(attr.getHasTrivialDtor()); +} + +TEST_F(RecordLayoutAttrTest, CanNeverPassInRegs) { + auto attr = RecordLayoutAttr::get( + &context, ArgPassingKind::CanNeverPassInRegs, false, 8); + EXPECT_EQ(attr.getArgPassingKind(), ArgPassingKind::CanNeverPassInRegs); + EXPECT_FALSE(attr.getHasTrivialDtor()); + EXPECT_EQ(attr.getRecordAlign(), 8u); +} + +TEST_F(RecordLayoutAttrTest, HighAlignment) { + auto attr = + RecordLayoutAttr::get(&context, ArgPassingKind::CanPassInRegs, true, 32); + EXPECT_EQ(attr.getRecordAlign(), 32u); +} + +TEST_F(RecordLayoutAttrTest, RecordTypeUnchanged) { + IntType i32 = IntType::get(&context, 32, true); + auto ty = + RecordType::get(&context, getName("Foo"), RecordType::RecordKind::Struct); + ty.complete({i32, i32}, /*packed=*/false, /*padded=*/false); + EXPECT_TRUE(ty.isComplete()); + EXPECT_EQ(ty.getMembers().size(), 2u); +} + +TEST_F(RecordLayoutAttrTest, ModuleLevelLookup) { + mlir::OpBuilder builder(&context); + auto loc = builder.getUnknownLoc(); + auto module = mlir::ModuleOp::create(loc); + + auto layoutAttr = + RecordLayoutAttr::get(&context, ArgPassingKind::CanPassInRegs, true, 8); + + llvm::SmallVector<mlir::NamedAttribute> entries; + entries.push_back(mlir::NamedAttribute(getName("TestRecord"), layoutAttr)); + module->setAttr(CIRDialect::getRecordLayoutsAttrName(), + mlir::DictionaryAttr::get(&context, entries)); + + RecordLayoutAttr result = cir::getRecordLayout(module, getName("TestRecord")); + EXPECT_EQ(result.getArgPassingKind(), ArgPassingKind::CanPassInRegs); + EXPECT_TRUE(result.getHasTrivialDtor()); + EXPECT_EQ(result.getRecordAlign(), 8u); + + module->erase(); +} diff --git a/llvm/utils/gn/secondary/clang/unittests/CIR/BUILD.gn b/llvm/utils/gn/secondary/clang/unittests/CIR/BUILD.gn index c480200e18c8c..4ae09493007eb 100644 --- a/llvm/utils/gn/secondary/clang/unittests/CIR/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/unittests/CIR/BUILD.gn @@ -1,5 +1,8 @@ # Dummy target because real CIRUnitTests depends on //mlir, which isn't # part of the GN build. group("CIRUnitTests") { - sources = [ "PointerLikeTest.cpp" ] + sources = [ + "PointerLikeTest.cpp", + "RecordTypeMetadataTest.cpp", + ] } >From 80d82a71b156f532e1d0a6775ee3563aae38e672 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Thu, 9 Apr 2026 11:53:35 -0700 Subject: [PATCH 2/2] [CIR][ABI] Extract convertRecordArgPassingKind helper Address xlauko's review nit: extract the RecordArgPassingKind-to-cir::ArgPassingKind switch into a separate function. Made-with: Cursor --- .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index f76f21ce60814..6947fd257f47f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -666,6 +666,19 @@ void CIRRecordLowering::insertPadding() { llvm::stable_sort(members); } +static cir::ArgPassingKind +convertRecordArgPassingKind(RecordArgPassingKind kind) { + switch (kind) { + case RecordArgPassingKind::CanPassInRegs: + return cir::ArgPassingKind::CanPassInRegs; + case RecordArgPassingKind::CannotPassInRegs: + return cir::ArgPassingKind::CannotPassInRegs; + case RecordArgPassingKind::CanNeverPassInRegs: + return cir::ArgPassingKind::CanNeverPassInRegs; + } + llvm_unreachable("unknown RecordArgPassingKind"); +} + std::unique_ptr<CIRGenRecordLayout> CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) { CIRRecordLowering lowering(*this, rd, /*packed=*/false); @@ -703,18 +716,8 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) { // Queue ABI metadata for the module-level cir.record_layouts attribute. if (ty->getName()) { mlir::MLIRContext *mlirCtx = ty->getContext(); - auto apk = cir::ArgPassingKind::CannotPassInRegs; - switch (rd->getArgPassingRestrictions()) { - case RecordArgPassingKind::CanPassInRegs: - apk = cir::ArgPassingKind::CanPassInRegs; - break; - case RecordArgPassingKind::CannotPassInRegs: - apk = cir::ArgPassingKind::CannotPassInRegs; - break; - case RecordArgPassingKind::CanNeverPassInRegs: - apk = cir::ArgPassingKind::CanNeverPassInRegs; - break; - } + cir::ArgPassingKind apk = + convertRecordArgPassingKind(rd->getArgPassingRestrictions()); bool hasTrivialDestructor = true; if (auto *cxxRD = dyn_cast<CXXRecordDecl>(rd)) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
