]> granicus.if.org Git - clang/commitdiff
Create proper Objective-C @encoding for C++ classes; fixes rdar://9357400.
authorArgyrios Kyrtzidis <akyrtzi@gmail.com>
Sat, 14 May 2011 20:32:43 +0000 (20:32 +0000)
committerArgyrios Kyrtzidis <akyrtzi@gmail.com>
Sat, 14 May 2011 20:32:43 +0000 (20:32 +0000)
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
lib/AST/ASTContext.cpp
test/CodeGenObjC/encode-test.m
test/CodeGenObjCXX/encode.mm

index 55aca03fe5645a2ab6bd6d3471c443ef680c7214..2e84c5b08da087f052bb9dd03baf44f9db33f803 100644 (file)
@@ -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,
index 379c13a9342cc6978a9534a303b1de0cc87e931b..2272bdae13eba365be5e67c564bf4ab78b3be348 100644 (file)
@@ -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)
index 24a90a0b4b998fa1b24b6a01d466d4c546488b8e..580b32cd0a11c5b408d29f09dfb5e1bf0ebf30c7 100644 (file)
@@ -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);
index 5a49feb706c9ac0bac7c5bab74d029ba42f633e0..1d65cb16ddc583d852f55ada94c8075b9caf8b21 100644 (file)
@@ -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);