Add size/alignment/offset to C++ IR and populate.

These values are not being serialized to JSON yet, so no need to change Rust
code -- that will happen in a followup CL.

PiperOrigin-RevId: 397309514
diff --git a/rs_bindings_from_cc/ast_visitor.cc b/rs_bindings_from_cc/ast_visitor.cc
index 5c88eeb..42871e5 100644
--- a/rs_bindings_from_cc/ast_visitor.cc
+++ b/rs_bindings_from_cc/ast_visitor.cc
@@ -17,6 +17,7 @@
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/Decl.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/DeclCXX.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/Mangle.h"
+#include "third_party/llvm/llvm-project/clang/include/clang/AST/RecordLayout.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/AST/Type.h"
 #include "third_party/llvm/llvm-project/clang/include/clang/Basic/Specifiers.h"
 #include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Casting.h"
@@ -97,6 +98,8 @@
       default_access = clang::AS_private;
     }
   }
+  const clang::ASTRecordLayout& layout =
+      record_decl->getASTContext().getASTRecordLayout(record_decl);
   for (const clang::FieldDecl* field_decl : record_decl->fields()) {
     auto type = ConvertType(field_decl->getType(), field_decl->getASTContext());
     if (!type.ok()) {
@@ -107,11 +110,16 @@
     if (access == clang::AS_none) {
       access = default_access;
     }
-    fields.push_back({.identifier = GetTranslatedName(field_decl),
-                      .type = *type,
-                      .access = TranslateAccessSpecifier(access)});
+    fields.push_back(
+        {.identifier = GetTranslatedName(field_decl),
+         .type = *type,
+         .access = TranslateAccessSpecifier(access),
+         .offset = layout.getFieldOffset(field_decl->getFieldIndex())});
   }
-  ir_.records.push_back({GetTranslatedName(record_decl), std::move(fields)});
+  ir_.records.push_back({.identifier = GetTranslatedName(record_decl),
+                         .fields = std::move(fields),
+                         .size = layout.getSize().getQuantity(),
+                         .alignment = layout.getAlignment().getQuantity()});
   return true;
 }
 
diff --git a/rs_bindings_from_cc/ast_visitor_test.cc b/rs_bindings_from_cc/ast_visitor_test.cc
index 23ace9d..8cc741a 100644
--- a/rs_bindings_from_cc/ast_visitor_test.cc
+++ b/rs_bindings_from_cc/ast_visitor_test.cc
@@ -107,9 +107,33 @@
   return testing::Field(&Record::fields, ElementsAre(matchers...));
 }
 
+// Matches a Record that has the given size.
+MATCHER_P(RecordSizeIs, size, "") {
+  if (arg.size == size) return true;
+
+  *result_listener << "actual size: " << arg.size;
+  return false;
+}
+
+// Matches a Record that has the given alignment.
+MATCHER_P(AlignmentIs, alignment, "") {
+  if (arg.alignment == alignment) return true;
+
+  *result_listener << "actual alignment: " << arg.alignment;
+  return false;
+}
+
 // Matches a Field that has the given access specifier.
 MATCHER_P(AccessIs, access, "") { return arg.access == access; }
 
+// Matches a Field that has the given offset.
+MATCHER_P(OffsetIs, offset, "") {
+  if (arg.offset == offset) return true;
+
+  *result_listener << "actual offset: " << arg.offset;
+  return false;
+}
+
 // Matches a Field with a type that matches all given matchers.
 template <typename... Args>
 auto FieldType(const Args&... matchers) {
@@ -227,12 +251,13 @@
       {"struct SomeStruct { int first_field; int second_field; };"}, {});
   EXPECT_THAT(ir.functions, IsEmpty());
 
-  EXPECT_THAT(
-      ir.records,
-      ElementsAre(AllOf(
-          IdentifierIs("SomeStruct"),
-          FieldsAre(AllOf(IdentifierIs("first_field"), FieldType(IsInt())),
-                    AllOf(IdentifierIs("second_field"), FieldType(IsInt()))))));
+  EXPECT_THAT(ir.records,
+              ElementsAre(AllOf(
+                  IdentifierIs("SomeStruct"), RecordSizeIs(8), AlignmentIs(4),
+                  FieldsAre(AllOf(IdentifierIs("first_field"),
+                                  FieldType(IsInt()), OffsetIs(0)),
+                            AllOf(IdentifierIs("second_field"),
+                                  FieldType(IsInt()), OffsetIs(32))))));
 }
 
 TEST(AstVisitorTest, MemberVariableAccessSpecifiers) {
diff --git a/rs_bindings_from_cc/ir.h b/rs_bindings_from_cc/ir.h
index 6fc6f24..4351839 100644
--- a/rs_bindings_from_cc/ir.h
+++ b/rs_bindings_from_cc/ir.h
@@ -146,6 +146,8 @@
   Identifier identifier;
   Type type;
   AccessSpecifier access;
+  // Field offset in bits.
+  uint64_t offset;
 };
 
 // A record (struct, class, union).
@@ -154,6 +156,9 @@
 
   Identifier identifier;
   std::vector<Field> fields;
+  // Size and alignment in bytes.
+  int64_t size;
+  int64_t alignment;
 };
 
 // A complete intermediate representation of bindings for publicly accessible