-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb][NFC] Move helpers to import record layout into ClangASTImporter #83291
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[lldb][NFC] Move helpers to import record layout into ClangASTImporter #83291
Conversation
@llvm/pr-subscribers-lldb Author: Michael Buch (Michael137) ChangesThis patch moves the logic for copying the layout info of a A follow-up patch re-uses the logic from within the Patch is 26.73 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/83291.diff 4 Files Affected:
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp
index 62a30c14912bc9..b9f2c834dd1197 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp
@@ -10,9 +10,11 @@
#include "lldb/Utility/LLDBAssert.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/RecordLayout.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
#include "llvm/Support/raw_ostream.h"
@@ -26,6 +28,7 @@
#include <memory>
#include <optional>
+#include <type_traits>
using namespace lldb_private;
using namespace clang;
@@ -517,6 +520,207 @@ bool ClangASTImporter::CompleteType(const CompilerType &compiler_type) {
return false;
}
+template <class D, class O>
+static bool ImportOffsetMap(clang::ASTContext *dest_ctx,
+ llvm::DenseMap<const D *, O> &destination_map,
+ llvm::DenseMap<const D *, O> &source_map,
+ ClangASTImporter &importer) {
+ // When importing fields into a new record, clang has a hard requirement that
+ // fields be imported in field offset order. Since they are stored in a
+ // DenseMap with a pointer as the key type, this means we cannot simply
+ // iterate over the map, as the order will be non-deterministic. Instead we
+ // have to sort by the offset and then insert in sorted order.
+ typedef llvm::DenseMap<const D *, O> MapType;
+ typedef typename MapType::value_type PairType;
+ std::vector<PairType> sorted_items;
+ sorted_items.reserve(source_map.size());
+ sorted_items.assign(source_map.begin(), source_map.end());
+ llvm::sort(sorted_items, llvm::less_second());
+
+ for (const auto &item : sorted_items) {
+ DeclFromUser<D> user_decl(const_cast<D *>(item.first));
+ DeclFromParser<D> parser_decl(user_decl.Import(dest_ctx, importer));
+ if (parser_decl.IsInvalid())
+ return false;
+ destination_map.insert(
+ std::pair<const D *, O>(parser_decl.decl, item.second));
+ }
+
+ return true;
+}
+
+template <bool IsVirtual>
+bool ExtractBaseOffsets(const ASTRecordLayout &record_layout,
+ DeclFromUser<const CXXRecordDecl> &record,
+ llvm::DenseMap<const clang::CXXRecordDecl *,
+ clang::CharUnits> &base_offsets) {
+ for (CXXRecordDecl::base_class_const_iterator
+ bi = (IsVirtual ? record->vbases_begin() : record->bases_begin()),
+ be = (IsVirtual ? record->vbases_end() : record->bases_end());
+ bi != be; ++bi) {
+ if (!IsVirtual && bi->isVirtual())
+ continue;
+
+ const clang::Type *origin_base_type = bi->getType().getTypePtr();
+ const clang::RecordType *origin_base_record_type =
+ origin_base_type->getAs<RecordType>();
+
+ if (!origin_base_record_type)
+ return false;
+
+ DeclFromUser<RecordDecl> origin_base_record(
+ origin_base_record_type->getDecl());
+
+ if (origin_base_record.IsInvalid())
+ return false;
+
+ DeclFromUser<CXXRecordDecl> origin_base_cxx_record(
+ DynCast<CXXRecordDecl>(origin_base_record));
+
+ if (origin_base_cxx_record.IsInvalid())
+ return false;
+
+ CharUnits base_offset;
+
+ if (IsVirtual)
+ base_offset =
+ record_layout.getVBaseClassOffset(origin_base_cxx_record.decl);
+ else
+ base_offset =
+ record_layout.getBaseClassOffset(origin_base_cxx_record.decl);
+
+ base_offsets.insert(std::pair<const CXXRecordDecl *, CharUnits>(
+ origin_base_cxx_record.decl, base_offset));
+ }
+
+ return true;
+}
+
+bool ClangASTImporter::importRecordLayoutFromOrigin(
+ const RecordDecl *record, uint64_t &size, uint64_t &alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &field_offsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &base_offsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &vbase_offsets) {
+
+ Log *log = GetLog(LLDBLog::Expressions);
+
+ clang::ASTContext &dest_ctx = record->getASTContext();
+ LLDB_LOG(log,
+ "LayoutRecordType on (ASTContext*){0} '{1}' for (RecordDecl*)"
+ "{2} [name = '{3}']",
+ &dest_ctx,
+ TypeSystemClang::GetASTContext(&dest_ctx)->getDisplayName(), record,
+ record->getName());
+
+ DeclFromParser<const RecordDecl> parser_record(record);
+ DeclFromUser<const RecordDecl> origin_record(parser_record.GetOrigin(*this));
+
+ if (origin_record.IsInvalid())
+ return false;
+
+ std::remove_reference_t<decltype(field_offsets)> origin_field_offsets;
+ std::remove_reference_t<decltype(base_offsets)> origin_base_offsets;
+ std::remove_reference_t<decltype(vbase_offsets)> origin_virtual_base_offsets;
+
+ TypeSystemClang::GetCompleteDecl(
+ &origin_record->getASTContext(),
+ const_cast<RecordDecl *>(origin_record.decl));
+
+ clang::RecordDecl *definition = origin_record.decl->getDefinition();
+ if (!definition || !definition->isCompleteDefinition())
+ return false;
+
+ const ASTRecordLayout &record_layout(
+ origin_record->getASTContext().getASTRecordLayout(origin_record.decl));
+
+ int field_idx = 0, field_count = record_layout.getFieldCount();
+
+ for (RecordDecl::field_iterator fi = origin_record->field_begin(),
+ fe = origin_record->field_end();
+ fi != fe; ++fi) {
+ if (field_idx >= field_count)
+ return false; // Layout didn't go well. Bail out.
+
+ uint64_t field_offset = record_layout.getFieldOffset(field_idx);
+
+ origin_field_offsets.insert(
+ std::pair<const FieldDecl *, uint64_t>(*fi, field_offset));
+
+ field_idx++;
+ }
+
+ DeclFromUser<const CXXRecordDecl> origin_cxx_record(
+ DynCast<const CXXRecordDecl>(origin_record));
+
+ if (origin_cxx_record.IsValid()) {
+ if (!ExtractBaseOffsets<false>(record_layout, origin_cxx_record,
+ origin_base_offsets) ||
+ !ExtractBaseOffsets<true>(record_layout, origin_cxx_record,
+ origin_virtual_base_offsets))
+ return false;
+ }
+
+ if (!ImportOffsetMap(&dest_ctx, field_offsets, origin_field_offsets, *this) ||
+ !ImportOffsetMap(&dest_ctx, base_offsets, origin_base_offsets, *this) ||
+ !ImportOffsetMap(&dest_ctx, vbase_offsets, origin_virtual_base_offsets,
+ *this))
+ return false;
+
+ size = record_layout.getSize().getQuantity() * dest_ctx.getCharWidth();
+ alignment =
+ record_layout.getAlignment().getQuantity() * dest_ctx.getCharWidth();
+
+ if (log) {
+ LLDB_LOG(log, "LRT returned:");
+ LLDB_LOG(log, "LRT Original = (RecordDecl*){0}",
+ static_cast<const void *>(origin_record.decl));
+ LLDB_LOG(log, "LRT Size = {0}", size);
+ LLDB_LOG(log, "LRT Alignment = {0}", alignment);
+ LLDB_LOG(log, "LRT Fields:");
+ for (RecordDecl::field_iterator fi = record->field_begin(),
+ fe = record->field_end();
+ fi != fe; ++fi) {
+ LLDB_LOG(log,
+ "LRT (FieldDecl*){0}, Name = '{1}', Type = '{2}', Offset = "
+ "{3} bits",
+ *fi, fi->getName(), fi->getType().getAsString(),
+ field_offsets[*fi]);
+ }
+ DeclFromParser<const CXXRecordDecl> parser_cxx_record =
+ DynCast<const CXXRecordDecl>(parser_record);
+ if (parser_cxx_record.IsValid()) {
+ LLDB_LOG(log, "LRT Bases:");
+ for (CXXRecordDecl::base_class_const_iterator
+ bi = parser_cxx_record->bases_begin(),
+ be = parser_cxx_record->bases_end();
+ bi != be; ++bi) {
+ bool is_virtual = bi->isVirtual();
+
+ QualType base_type = bi->getType();
+ const RecordType *base_record_type = base_type->getAs<RecordType>();
+ DeclFromParser<RecordDecl> base_record(base_record_type->getDecl());
+ DeclFromParser<CXXRecordDecl> base_cxx_record =
+ DynCast<CXXRecordDecl>(base_record);
+
+ LLDB_LOG(log,
+ "LRT {0}(CXXRecordDecl*){1}, Name = '{2}', Offset = "
+ "{3} chars",
+ (is_virtual ? "Virtual " : ""), base_cxx_record.decl,
+ base_cxx_record.decl->getName(),
+ (is_virtual
+ ? vbase_offsets[base_cxx_record.decl].getQuantity()
+ : base_offsets[base_cxx_record.decl].getQuantity()));
+ }
+ } else {
+ LLDB_LOG(log, "LRD Not a CXXRecord, so no bases");
+ }
+ }
+
+ return true;
+}
+
bool ClangASTImporter::LayoutRecordType(
const clang::RecordDecl *record_decl, uint64_t &bit_size,
uint64_t &alignment,
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h
index e565a96b217ff4..f9c370bef94fc9 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h
@@ -14,6 +14,7 @@
#include <set>
#include <vector>
+#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTImporter.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/Decl.h"
@@ -127,6 +128,14 @@ class ClangASTImporter {
llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
&vbase_offsets);
+ bool importRecordLayoutFromOrigin(
+ const clang::RecordDecl *record, uint64_t &size, uint64_t &alignment,
+ llvm::DenseMap<const clang::FieldDecl *, uint64_t> &field_offsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &base_offsets,
+ llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits>
+ &vbase_offsets);
+
/// Returns true iff the given type was copied from another TypeSystemClang
/// and the original type in this other TypeSystemClang might contain
/// additional information (e.g., the definition of a 'class' type) that could
@@ -456,6 +465,58 @@ class ClangASTImporter {
RecordDeclToLayoutMap m_record_decl_to_layout_map;
};
+template <class D> class TaggedASTDecl {
+public:
+ TaggedASTDecl() : decl(nullptr) {}
+ TaggedASTDecl(D *_decl) : decl(_decl) {}
+ bool IsValid() const { return (decl != nullptr); }
+ bool IsInvalid() const { return !IsValid(); }
+ D *operator->() const { return decl; }
+ D *decl;
+};
+
+template <class D2, template <class D> class TD, class D1>
+TD<D2> DynCast(TD<D1> source) {
+ return TD<D2>(llvm::dyn_cast<D2>(source.decl));
+}
+
+template <class D = clang::Decl> class DeclFromParser;
+template <class D = clang::Decl> class DeclFromUser;
+
+template <class D> class DeclFromParser : public TaggedASTDecl<D> {
+public:
+ DeclFromParser() : TaggedASTDecl<D>() {}
+ DeclFromParser(D *_decl) : TaggedASTDecl<D>(_decl) {}
+
+ DeclFromUser<D> GetOrigin(ClangASTImporter &importer);
+};
+
+template <class D> class DeclFromUser : public TaggedASTDecl<D> {
+public:
+ DeclFromUser() : TaggedASTDecl<D>() {}
+ DeclFromUser(D *_decl) : TaggedASTDecl<D>(_decl) {}
+
+ DeclFromParser<D> Import(clang::ASTContext *dest_ctx,
+ ClangASTImporter &importer);
+};
+
+template <class D>
+DeclFromUser<D> DeclFromParser<D>::GetOrigin(ClangASTImporter &importer) {
+ ClangASTImporter::DeclOrigin origin = importer.GetDeclOrigin(this->decl);
+ if (!origin.Valid())
+ return DeclFromUser<D>();
+ return DeclFromUser<D>(llvm::dyn_cast<D>(origin.decl));
+}
+
+template <class D>
+DeclFromParser<D> DeclFromUser<D>::Import(clang::ASTContext *dest_ctx,
+ ClangASTImporter &importer) {
+ DeclFromParser<> parser_generic_decl(importer.CopyDecl(dest_ctx, this->decl));
+ if (parser_generic_decl.IsInvalid())
+ return DeclFromParser<D>();
+ return DeclFromParser<D>(llvm::dyn_cast<D>(parser_generic_decl.decl));
+}
+
} // namespace lldb_private
#endif // LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CLANG_CLANGASTIMPORTER_H
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
index 79dd306f7627fd..e88e2be5e5652a 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
@@ -21,6 +21,7 @@
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclVisitor.h"
#include "clang/AST/RecordLayout.h"
#include "clang/Basic/SourceManager.h"
@@ -705,56 +706,6 @@ void ClangASTSource::FillNamespaceMap(
}
}
-template <class D> class TaggedASTDecl {
-public:
- TaggedASTDecl() : decl(nullptr) {}
- TaggedASTDecl(D *_decl) : decl(_decl) {}
- bool IsValid() const { return (decl != nullptr); }
- bool IsInvalid() const { return !IsValid(); }
- D *operator->() const { return decl; }
- D *decl;
-};
-
-template <class D2, template <class D> class TD, class D1>
-TD<D2> DynCast(TD<D1> source) {
- return TD<D2>(dyn_cast<D2>(source.decl));
-}
-
-template <class D = Decl> class DeclFromParser;
-template <class D = Decl> class DeclFromUser;
-
-template <class D> class DeclFromParser : public TaggedASTDecl<D> {
-public:
- DeclFromParser() : TaggedASTDecl<D>() {}
- DeclFromParser(D *_decl) : TaggedASTDecl<D>(_decl) {}
-
- DeclFromUser<D> GetOrigin(ClangASTSource &source);
-};
-
-template <class D> class DeclFromUser : public TaggedASTDecl<D> {
-public:
- DeclFromUser() : TaggedASTDecl<D>() {}
- DeclFromUser(D *_decl) : TaggedASTDecl<D>(_decl) {}
-
- DeclFromParser<D> Import(ClangASTSource &source);
-};
-
-template <class D>
-DeclFromUser<D> DeclFromParser<D>::GetOrigin(ClangASTSource &source) {
- ClangASTImporter::DeclOrigin origin = source.GetDeclOrigin(this->decl);
- if (!origin.Valid())
- return DeclFromUser<D>();
- return DeclFromUser<D>(dyn_cast<D>(origin.decl));
-}
-
-template <class D>
-DeclFromParser<D> DeclFromUser<D>::Import(ClangASTSource &source) {
- DeclFromParser<> parser_generic_decl(source.CopyDecl(this->decl));
- if (parser_generic_decl.IsInvalid())
- return DeclFromParser<D>();
- return DeclFromParser<D>(dyn_cast<D>(parser_generic_decl.decl));
-}
-
bool ClangASTSource::FindObjCMethodDeclsWithOrigin(
NameSearchContext &context, ObjCInterfaceDecl *original_interface_decl,
const char *log_info) {
@@ -1188,8 +1139,8 @@ void ClangASTSource::FindObjCMethodDecls(NameSearchContext &context) {
} while (false);
}
-static bool FindObjCPropertyAndIvarDeclsWithOrigin(
- NameSearchContext &context, ClangASTSource &source,
+bool ClangASTSource::FindObjCPropertyAndIvarDeclsWithOrigin(
+ NameSearchContext &context,
DeclFromUser<const ObjCInterfaceDecl> &origin_iface_decl) {
Log *log = GetLog(LLDBLog::Expressions);
@@ -1209,7 +1160,7 @@ static bool FindObjCPropertyAndIvarDeclsWithOrigin(
if (origin_property_decl.IsValid()) {
DeclFromParser<ObjCPropertyDecl> parser_property_decl(
- origin_property_decl.Import(source));
+ origin_property_decl.Import(m_ast_context, *m_ast_importer_sp));
if (parser_property_decl.IsValid()) {
LLDB_LOG(log, " CAS::FOPD found\n{0}",
ClangUtil::DumpDecl(parser_property_decl.decl));
@@ -1224,7 +1175,7 @@ static bool FindObjCPropertyAndIvarDeclsWithOrigin(
if (origin_ivar_decl.IsValid()) {
DeclFromParser<ObjCIvarDecl> parser_ivar_decl(
- origin_ivar_decl.Import(source));
+ origin_ivar_decl.Import(m_ast_context, *m_ast_importer_sp));
if (parser_ivar_decl.IsValid()) {
LLDB_LOG(log, " CAS::FOPD found\n{0}",
ClangUtil::DumpDecl(parser_ivar_decl.decl));
@@ -1243,7 +1194,7 @@ void ClangASTSource::FindObjCPropertyAndIvarDecls(NameSearchContext &context) {
DeclFromParser<const ObjCInterfaceDecl> parser_iface_decl(
cast<ObjCInterfaceDecl>(context.m_decl_context));
DeclFromUser<const ObjCInterfaceDecl> origin_iface_decl(
- parser_iface_decl.GetOrigin(*this));
+ parser_iface_decl.GetOrigin(*m_ast_importer_sp));
ConstString class_name(parser_iface_decl->getNameAsString().c_str());
@@ -1253,7 +1204,7 @@ void ClangASTSource::FindObjCPropertyAndIvarDecls(NameSearchContext &context) {
m_ast_context, m_clang_ast_context->getDisplayName(),
parser_iface_decl->getName(), context.m_decl_name.getAsString());
- if (FindObjCPropertyAndIvarDeclsWithOrigin(context, *this, origin_iface_decl))
+ if (FindObjCPropertyAndIvarDeclsWithOrigin(context, origin_iface_decl))
return;
LLDB_LOG(log,
@@ -1286,7 +1237,7 @@ void ClangASTSource::FindObjCPropertyAndIvarDecls(NameSearchContext &context) {
"(ObjCInterfaceDecl*){0}/(ASTContext*){1}...",
complete_iface_decl.decl, &complete_iface_decl->getASTContext());
- FindObjCPropertyAndIvarDeclsWithOrigin(context, *this, complete_iface_decl);
+ FindObjCPropertyAndIvarDeclsWithOrigin(context, complete_iface_decl);
return;
} while (false);
@@ -1320,7 +1271,7 @@ void ClangASTSource::FindObjCPropertyAndIvarDecls(NameSearchContext &context) {
interface_decl_from_modules.decl,
&interface_decl_from_modules->getASTContext());
- if (FindObjCPropertyAndIvarDeclsWithOrigin(context, *this,
+ if (FindObjCPropertyAndIvarDeclsWithOrigin(context,
interface_decl_from_modules))
return;
} while (false);
@@ -1364,7 +1315,7 @@ void ClangASTSource::FindObjCPropertyAndIvarDecls(NameSearchContext &context) {
interface_decl_from_runtime.decl,
&interface_decl_from_runtime->getASTContext());
- if (FindObjCPropertyAndIvarDeclsWithOrigin(context, *this,
+ if (FindObjCPropertyAndIvarDeclsWithOrigin(context,
interface_decl_from_runtime))
return;
} while (false);
@@ -1395,205 +1346,16 @@ void ClangASTSource::LookupInNamespace(NameSearchContext &context) {
}
}
-typedef llvm::DenseMap<const FieldDecl *, uint64_t> FieldOffsetMap;
-typedef llvm::DenseMap<const CXXRecordDecl *, CharUnits> BaseOffsetMap;
-
-template <class D, class O>
-static bool ImportOffsetMap(llvm::DenseMap<const D *, O> &destination_map,
- llvm::DenseMap<const D *, O> &source_map,
- ClangASTSource &source) {
- // When importing fields into a new record, clang has a hard requirement that
- // fields be imported in field offset order. Since they are stored in a
- // DenseMap with a pointer as the key type, this means we cannot simply
- // iterate over the map, as the order will be non-deterministic. Instead we
- // have to sort by the offset and then insert in sorted order.
- typedef llvm::DenseMap<const D *, O> MapType;
- typedef typename MapType::value_type PairType;
- std::vector<PairType> sorted_items;
- sorted_items.reserve(source_map.size());
- sorted_items.assign(source_map.begin(), source_map.end());
- llvm::sort(sorted_items, llvm::less_second());
-
- for (const auto &item : sorted_items) {
- DeclFromUser<D> user_decl(const_cast<D *>(item.first));
- DeclFromParser<D> parser_decl(user_decl.Import(source));
- if (parser_decl.IsInvalid())
- return false;
- destination_map.insert(
- std::pair<const D *, O>(parser_decl.decl, item.second));
- }
-
- return true;
-}
-
-template <bool IsVirtual>
-bool ExtractBaseOffsets(const ASTRecordLayout &record_layout,
- DeclFromUser<const CXXRecordDecl> &record,
- BaseOffsetMap &base_offsets) {
- for (CXXRecordDecl::base_class_const_iterator
- bi = (IsVirtual ? record->vbases_begin() : record->bases_begin()),
- be = (IsVirtual ? record->vbases_end() : record->bases_end());
- bi != be; ++bi) {
- if (!IsVirtual && bi->isVirtual())
- continue;
-
- const clang::Type *origin_base_type = bi->getType().getTypePtr();
- const clang::RecordType *origin_base_record_type =
- origin_base_type->getAs<RecordType>();
-
- if (!origin_base_record_type)
- return false;
-
- DeclFromUser<RecordDecl> origin_ba...
[truncated]
|
@@ -517,6 +520,207 @@ bool ClangASTImporter::CompleteType(const CompilerType &compiler_type) { | |||
return false; | |||
} | |||
|
|||
template <class D, class O> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not your code, but I'm wondering if there's an opportunity here to add a small doxygen comment to the top of the functions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
✅ With the latest revision this PR passed the C/C++ code formatter. |
llvm#83291) This patch moves the logic for copying the layout info of a `RecordDecl`s origin into a target AST. A follow-up patch re-uses the logic from within the `ClangASTImporter`, so the natural choice was to move it there. (cherry picked from commit 8c10032)
This patch moves the logic for copying the layout info of a
RecordDecl
s origin into a target AST.A follow-up patch re-uses the logic from within the
ClangASTImporter
, so the natural choice was to move it there.