From 02c5116db6a40e806534c416e0abb22b8840831e Mon Sep 17 00:00:00 2001
From: Argyrios Kyrtzidis <akyrtzi@gmail.com>
Date: Sat, 14 May 2011 20:32:43 +0000
Subject: [PATCH] Create proper Objective-C @encoding for C++ classes; fixes
 rdar://9357400.

Go through and expand the members of bases into the encoding string (and encode the VTable as well).
Unlike gcc which expands virtual bases as many times as they appear in the
hierarchy, clang will only expand them once at the end, to reflect the actual layout.

Note that there doesn't seem to be a way to indicate in the encoding that
packing/alignment of members is different that normal, in which case
the encoding will be out-of-sync with the real layout.
If the runtime switches to just consider the size of types without
taking into account alignment, we could easily make padding explicit in the
encoding (e.g. using arrays of chars). The encoding strings would be
longer then though.

Also encode a flexible array member as array of 0 size, like gcc, not as a pointer.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@131365 91177308-0d34-0410-b5e6-96231b3b80d8
---
 include/clang/AST/ASTContext.h |   8 +-
 lib/AST/ASTContext.cpp         | 172 +++++++++++++++++++++++++++++----
 test/CodeGenObjC/encode-test.m |   7 ++
 test/CodeGenObjCXX/encode.mm   |  57 +++++++++++
 4 files changed, 223 insertions(+), 21 deletions(-)

diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h
index 55aca03fe56..2e84c5b08da 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -1568,7 +1568,13 @@ private:
                                   bool ExpandStructures,
                                   const FieldDecl *Field,
                                   bool OutermostType = false,
-                                  bool EncodingProperty = false) const;
+                                  bool EncodingProperty = false,
+                                  bool StructField = false) const;
+
+  // Adds the encoding of the structure's members.
+  void getObjCEncodingForStructureImpl(RecordDecl *RD, std::string &S,
+                                       const FieldDecl *Field,
+                                       bool includeVBases = true) const;
  
   const ASTRecordLayout &
   getObjCLayout(const ObjCInterfaceDecl *D,
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 379c13a9342..2272bdae13e 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -31,6 +31,7 @@
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/raw_ostream.h"
 #include "CXXABI.h"
+#include <map>
 
 using namespace clang;
 
@@ -4152,7 +4153,8 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string& S,
                                             bool ExpandStructures,
                                             const FieldDecl *FD,
                                             bool OutermostType,
-                                            bool EncodingProperty) const {
+                                            bool EncodingProperty,
+                                            bool StructField) const {
   if (T->getAs<BuiltinType>()) {
     if (FD && FD->isBitField())
       return EncodeBitField(this, S, T, FD);
@@ -4237,7 +4239,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string& S,
   if (const ArrayType *AT =
       // Ignore type qualifiers etc.
         dyn_cast<ArrayType>(T->getCanonicalTypeInternal())) {
-    if (isa<IncompleteArrayType>(AT)) {
+    if (isa<IncompleteArrayType>(AT) && !StructField) {
       // Incomplete arrays are encoded as a pointer to the array element.
       S += '^';
 
@@ -4250,7 +4252,8 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string& S,
         S += llvm::utostr(CAT->getSize().getZExtValue());
       else {
         //Variable length arrays are encoded as a regular array with 0 elements.
-        assert(isa<VariableArrayType>(AT) && "Unknown array type!");
+        assert((isa<VariableArrayType>(AT) || isa<IncompleteArrayType>(AT)) &&
+               "Unknown array type!");
         S += '0';
       }
 
@@ -4288,24 +4291,30 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string& S,
     }
     if (ExpandStructures) {
       S += '=';
-      for (RecordDecl::field_iterator Field = RDecl->field_begin(),
-                                   FieldEnd = RDecl->field_end();
-           Field != FieldEnd; ++Field) {
-        if (FD) {
-          S += '"';
-          S += Field->getNameAsString();
-          S += '"';
-        }
+      if (!RDecl->isUnion()) {
+        getObjCEncodingForStructureImpl(RDecl, S, FD);
+      } else {
+        for (RecordDecl::field_iterator Field = RDecl->field_begin(),
+                                     FieldEnd = RDecl->field_end();
+             Field != FieldEnd; ++Field) {
+          if (FD) {
+            S += '"';
+            S += Field->getNameAsString();
+            S += '"';
+          }
 
-        // Special case bit-fields.
-        if (Field->isBitField()) {
-          getObjCEncodingForTypeImpl(Field->getType(), S, false, true,
-                                     (*Field));
-        } else {
-          QualType qt = Field->getType();
-          getLegacyIntegralTypeEncoding(qt);
-          getObjCEncodingForTypeImpl(qt, S, false, true,
-                                     FD);
+          // Special case bit-fields.
+          if (Field->isBitField()) {
+            getObjCEncodingForTypeImpl(Field->getType(), S, false, true,
+                                       (*Field));
+          } else {
+            QualType qt = Field->getType();
+            getLegacyIntegralTypeEncoding(qt);
+            getObjCEncodingForTypeImpl(qt, S, false, true,
+                                       FD, /*OutermostType*/false,
+                                       /*EncodingProperty*/false,
+                                       /*StructField*/true);
+          }
         }
       }
     }
@@ -4426,6 +4435,129 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string& S,
   assert(0 && "@encode for type not implemented!");
 }
 
+void ASTContext::getObjCEncodingForStructureImpl(RecordDecl *RDecl,
+                                                 std::string &S,
+                                                 const FieldDecl *FD,
+                                                 bool includeVBases) const {
+  assert(RDecl && "Expected non-null RecordDecl");
+  assert(!RDecl->isUnion() && "Should not be called for unions");
+  if (!RDecl->getDefinition())
+    return;
+
+  CXXRecordDecl *CXXRec = dyn_cast<CXXRecordDecl>(RDecl);
+  std::map<uint64_t, NamedDecl *> FieldOrBaseOffsets;
+  const ASTRecordLayout &layout = getASTRecordLayout(RDecl);
+  
+  unsigned i = 0;
+  for (RecordDecl::field_iterator Field = RDecl->field_begin(),
+                               FieldEnd = RDecl->field_end();
+       Field != FieldEnd; ++Field, ++i) {
+    assert(!FieldOrBaseOffsets[layout.getFieldOffset(i)]);
+    FieldOrBaseOffsets[layout.getFieldOffset(i)] = *Field;
+  }
+  
+  if (CXXRec) {
+    for (CXXRecordDecl::base_class_iterator
+           BI = CXXRec->bases_begin(),
+           BE = CXXRec->bases_end(); BI != BE; ++BI) {
+      if (!BI->isVirtual()) {
+        CXXRecordDecl *base = BI->getType()->getAsCXXRecordDecl();
+        assert(!FieldOrBaseOffsets[layout.getBaseClassOffsetInBits(base)]);
+        FieldOrBaseOffsets[layout.getBaseClassOffsetInBits(base)] = base;
+      }
+    }
+    if (includeVBases) {
+      for (CXXRecordDecl::base_class_iterator
+             BI = CXXRec->vbases_begin(),
+             BE = CXXRec->vbases_end(); BI != BE; ++BI) {
+        CXXRecordDecl *base = BI->getType()->getAsCXXRecordDecl();
+        assert(!FieldOrBaseOffsets[layout.getVBaseClassOffsetInBits(base)]);
+        FieldOrBaseOffsets[layout.getVBaseClassOffsetInBits(base)] = base;
+      }
+    }
+  }
+
+  CharUnits size;
+  if (CXXRec) {
+    size = includeVBases ? layout.getSize() : layout.getNonVirtualSize();
+  } else {
+    size = layout.getSize();
+  }
+
+  uint64_t CurOffs = 0;
+  std::map<uint64_t, NamedDecl *>::iterator
+    CurLayObj = FieldOrBaseOffsets.begin();
+
+  if (CurLayObj != FieldOrBaseOffsets.end() && CurLayObj->first != 0) {
+    assert(CXXRec && CXXRec->isDynamicClass() &&
+           "Offset 0 was empty but no VTable ?");
+    if (FD) {
+      S += "\"_vptr$";
+      std::string recname = CXXRec->getNameAsString();
+      if (recname.empty()) recname = "?";
+      S += recname;
+      S += '"';
+    }
+    S += "^^?";
+    CurOffs += getTypeSize(VoidPtrTy);
+  }
+
+  if (!RDecl->hasFlexibleArrayMember()) {
+    // Mark the end of the structure.
+    assert(!FieldOrBaseOffsets[toBits(size)]);
+    FieldOrBaseOffsets[toBits(size)] = 0;
+  }
+
+  for (; CurLayObj != FieldOrBaseOffsets.end(); ++CurLayObj) {
+    assert(CurOffs <= CurLayObj->first);
+
+    if (CurOffs < CurLayObj->first) {
+      uint64_t padding = CurLayObj->first - CurOffs; 
+      // FIXME: There doesn't seem to be a way to indicate in the encoding that
+      // packing/alignment of members is different that normal, in which case
+      // the encoding will be out-of-sync with the real layout.
+      // If the runtime switches to just consider the size of types without
+      // taking into account alignment, we could make padding explicit in the
+      // encoding (e.g. using arrays of chars). The encoding strings would be
+      // longer then though.
+      CurOffs += padding;
+    }
+
+    NamedDecl *dcl = CurLayObj->second;
+    if (dcl == 0)
+      break; // reached end of structure.
+
+    if (CXXRecordDecl *base = dyn_cast<CXXRecordDecl>(dcl)) {
+      // We expand the bases without their virtual bases since those are going
+      // in the initial structure. Note that this differs from gcc which
+      // expands virtual bases each time one is encountered in the hierarchy,
+      // making the encoding type bigger than it really is.
+      getObjCEncodingForStructureImpl(base, S, FD, /*includeVBases*/false);
+      CurOffs += toBits(getASTRecordLayout(base).getNonVirtualSize());
+    } else {
+      FieldDecl *field = cast<FieldDecl>(dcl);
+      if (FD) {
+        S += '"';
+        S += field->getNameAsString();
+        S += '"';
+      }
+
+      if (field->isBitField()) {
+        EncodeBitField(this, S, field->getType(), field);
+        CurOffs += field->getBitWidth()->EvaluateAsInt(*this).getZExtValue();
+      } else {
+        QualType qt = field->getType();
+        getLegacyIntegralTypeEncoding(qt);
+        getObjCEncodingForTypeImpl(qt, S, false, true, FD,
+                                   /*OutermostType*/false,
+                                   /*EncodingProperty*/false,
+                                   /*StructField*/true);
+        CurOffs += getTypeSize(field->getType());
+      }
+    }
+  }
+}
+
 void ASTContext::getObjCEncodingForTypeQualifier(Decl::ObjCDeclQualifier QT,
                                                  std::string& S) const {
   if (QT & Decl::OBJC_TQ_In)
diff --git a/test/CodeGenObjC/encode-test.m b/test/CodeGenObjC/encode-test.m
index 24a90a0b4b9..580b32cd0a1 100644
--- a/test/CodeGenObjC/encode-test.m
+++ b/test/CodeGenObjC/encode-test.m
@@ -144,3 +144,10 @@ struct s8 {
   long double x;
 };
 const char g8[] = @encode(struct s8);
+
+// CHECK: @g9 = constant [11 x i8] c"{S9=i[0i]}\00"
+struct S9 {
+  int x;
+  int flex[];
+};
+const char g9[] = @encode(struct S9);
diff --git a/test/CodeGenObjCXX/encode.mm b/test/CodeGenObjCXX/encode.mm
index 5a49feb706c..1d65cb16ddc 100644
--- a/test/CodeGenObjCXX/encode.mm
+++ b/test/CodeGenObjCXX/encode.mm
@@ -62,3 +62,60 @@ typedef float HGVec4f __attribute__ ((vector_size(16)));
 @implementation RedBalloonHGXFormWrapper
 @end
 
+// rdar://9357400
+namespace rdar9357400 {
+  template<int Dim1 = -1, int Dim2 = -1> struct fixed {
+      template<int D> struct rebind { typedef fixed<D> other; };
+  };
+  
+  template<typename Element, int Size>
+  class fixed_1D
+  {
+  public:
+      typedef Element value_type;
+      typedef value_type array_impl[Size];
+    protected:
+      array_impl                  m_data;
+  };
+  
+  template<typename Element, typename Alloc>
+  class vector;
+  
+  template<typename Element, int Size>
+  class vector< Element, fixed<Size> >
+  : public fixed_1D<Element,Size> { };
+
+  typedef vector< float,  fixed<4> > vector4f;
+
+  // CHECK: @_ZN11rdar9357400L2ggE = internal constant [49 x i8] c"{vector<float, rdar9357400::fixed<4, -1> >=[4f]}\00"
+  const char gg[] = @encode(vector4f);
+}
+
+struct Base1 {
+  char x;
+};
+
+struct DBase : public Base1 {
+  double x;
+  virtual ~DBase();
+};
+
+struct Sub_with_virt : virtual DBase {
+  long x;
+};
+
+struct Sub2 : public Sub_with_virt, public Base1, virtual DBase {
+  float x;
+};
+
+// CHECK: @_ZL2g1 = internal constant [10 x i8] c"{Base1=c}\00"
+const char g1[] = @encode(Base1);
+
+// CHECK: @_ZL2g2 = internal constant [14 x i8] c"{DBase=^^?cd}\00"
+const char g2[] = @encode(DBase);
+
+// CHECK: @_ZL2g3 = internal constant [26 x i8] c"{Sub_with_virt=^^?q^^?cd}\00"
+const char g3[] = @encode(Sub_with_virt);
+
+// CHECK: @_ZL2g4 = internal constant [19 x i8] c"{Sub2=^^?qcf^^?cd}\00"
+const char g4[] = @encode(Sub2);
-- 
GitLab