r200585 - [ms-cxxabi] Raise aggregate memptr alignment to 8 for x86_32

Reid Kleckner reid at kleckner.net
Fri Jan 31 14:28:51 PST 2014


Author: rnk
Date: Fri Jan 31 16:28:50 2014
New Revision: 200585

URL: http://llvm.org/viewvc/llvm-project?rev=200585&view=rev
Log:
[ms-cxxabi] Raise aggregate memptr alignment to 8 for x86_32

With this change, we give different results for __alignof than MSVC, but
our record layout is compatible.

Some data member pointers also now have a size that is not a multiple of
their alignment.

Fixes PR18618.

Reviewers: majnemer

Differential Revision: http://llvm-reviews.chandlerc.com/D2669

Added:
    cfe/trunk/test/Layout/ms-x86-member-pointers.cpp
Modified:
    cfe/trunk/lib/AST/MicrosoftCXXABI.cpp
    cfe/trunk/test/CodeGenCXX/microsoft-abi-member-pointers.cpp
    cfe/trunk/test/SemaCXX/member-pointer-ms.cpp

Modified: cfe/trunk/lib/AST/MicrosoftCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/MicrosoftCXXABI.cpp?rev=200585&r1=200584&r2=200585&view=diff
==============================================================================
--- cfe/trunk/lib/AST/MicrosoftCXXABI.cpp (original)
+++ cfe/trunk/lib/AST/MicrosoftCXXABI.cpp Fri Jan 31 16:28:50 2014
@@ -197,8 +197,20 @@ std::pair<uint64_t, unsigned> MicrosoftC
   unsigned PtrSize = Target.getPointerWidth(0);
   unsigned IntSize = Target.getIntWidth();
   uint64_t Width = Ptrs * PtrSize + Ints * IntSize;
-  unsigned Align = Ptrs > 0 ? Target.getPointerAlign(0) : Target.getIntAlign();
-  Width = llvm::RoundUpToAlignment(Width, Align);
+  unsigned Align;
+
+  // When MSVC does x86_32 record layout, it aligns aggregate member pointers to
+  // 8 bytes.  However, __alignof usually returns 4 for data memptrs and 8 for
+  // function memptrs.
+  if (Ptrs + Ints > 1 && Target.getTriple().getArch() == llvm::Triple::x86)
+    Align = 8 * 8;
+  else if (Ptrs)
+    Align = Target.getPointerAlign(0);
+  else
+    Align = Target.getIntAlign();
+
+  if (Target.getTriple().getArch() == llvm::Triple::x86_64)
+    Width = llvm::RoundUpToAlignment(Width, Align);
   return std::make_pair(Width, Align);
 }
 

Modified: cfe/trunk/test/CodeGenCXX/microsoft-abi-member-pointers.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-member-pointers.cpp?rev=200585&r1=200584&r2=200585&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-member-pointers.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-member-pointers.cpp Fri Jan 31 16:28:50 2014
@@ -56,20 +56,20 @@ int UnspecSingle::*us_d_memptr;
 // CHECK: @"\01?p_d_memptr@@3PQPolymorphic@@HQ1@" = global i32 0, align 4
 // CHECK: @"\01?m_d_memptr@@3PQMultiple@@HQ1@" = global i32 -1, align 4
 // CHECK: @"\01?v_d_memptr@@3PQVirtual@@HQ1@" = global { i32, i32 }
-// CHECK:   { i32 0, i32 -1 }, align 4
+// CHECK:   { i32 0, i32 -1 }, align 8
 // CHECK: @"\01?n_d_memptr@@3PQNonZeroVBPtr@@HQ1@" = global { i32, i32 }
-// CHECK:   { i32 0, i32 -1 }, align 4
+// CHECK:   { i32 0, i32 -1 }, align 8
 // CHECK: @"\01?u_d_memptr@@3PQUnspecified@@HQ1@" = global { i32, i32, i32 }
-// CHECK:   { i32 0, i32 0, i32 -1 }, align 4
+// CHECK:   { i32 0, i32 0, i32 -1 }, align 8
 // CHECK: @"\01?us_d_memptr@@3PQUnspecSingle@@HQ1@" = global { i32, i32, i32 }
-// CHECK:   { i32 0, i32 0, i32 -1 }, align 4
+// CHECK:   { i32 0, i32 0, i32 -1 }, align 8
 
 void (Single  ::*s_f_memptr)();
 void (Multiple::*m_f_memptr)();
 void (Virtual ::*v_f_memptr)();
 // CHECK: @"\01?s_f_memptr@@3P8Single@@AEXXZQ1@" = global i8* null, align 4
-// CHECK: @"\01?m_f_memptr@@3P8Multiple@@AEXXZQ1@" = global { i8*, i32 } zeroinitializer, align 4
-// CHECK: @"\01?v_f_memptr@@3P8Virtual@@AEXXZQ1@" = global { i8*, i32, i32 } zeroinitializer, align 4
+// CHECK: @"\01?m_f_memptr@@3P8Multiple@@AEXXZQ1@" = global { i8*, i32 } zeroinitializer, align 8
+// CHECK: @"\01?v_f_memptr@@3P8Virtual@@AEXXZQ1@" = global { i8*, i32, i32 } zeroinitializer, align 8
 
 // We can define Unspecified after locking in the inheritance model.
 struct Unspecified : Multiple, Virtual {
@@ -91,13 +91,13 @@ void (UnspecSingle::*us_f_mp)() = &Unspe
 // CHECK: @"\01?s_f_mp at Const@@3P8Single@@AEXXZQ2@" =
 // CHECK:   global i8* bitcast ({{.*}} @"\01?foo at Single@@QAEXXZ" to i8*), align 4
 // CHECK: @"\01?m_f_mp at Const@@3P8Multiple@@AEXXZQ2@" =
-// CHECK:   global { i8*, i32 } { i8* bitcast ({{.*}} @"\01?foo at B2@@QAEXXZ" to i8*), i32 4 }, align 4
+// CHECK:   global { i8*, i32 } { i8* bitcast ({{.*}} @"\01?foo at B2@@QAEXXZ" to i8*), i32 4 }, align 8
 // CHECK: @"\01?v_f_mp at Const@@3P8Virtual@@AEXXZQ2@" =
-// CHECK:   global { i8*, i32, i32 } { i8* bitcast ({{.*}} @"\01?foo at Virtual@@QAEXXZ" to i8*), i32 0, i32 0 }, align 4
+// CHECK:   global { i8*, i32, i32 } { i8* bitcast ({{.*}} @"\01?foo at Virtual@@QAEXXZ" to i8*), i32 0, i32 0 }, align 8
 // CHECK: @"\01?u_f_mp at Const@@3P8Unspecified@@AEXXZQ2@" =
-// CHECK:   global { i8*, i32, i32, i32 } { i8* bitcast ({{.*}} @"\01?foo at Unspecified@@QAEXXZ" to i8*), i32 0, i32 12, i32 0 }, align 4
+// CHECK:   global { i8*, i32, i32, i32 } { i8* bitcast ({{.*}} @"\01?foo at Unspecified@@QAEXXZ" to i8*), i32 0, i32 12, i32 0 }, align 8
 // CHECK: @"\01?us_f_mp at Const@@3P8UnspecSingle@@AEXXZQ2@" =
-// CHECK:   global { i8*, i32, i32, i32 } { i8* bitcast ({{.*}} @"\01?foo at UnspecSingle@@QAEXXZ" to i8*), i32 0, i32 0, i32 0 }, align 4
+// CHECK:   global { i8*, i32, i32, i32 } { i8* bitcast ({{.*}} @"\01?foo at UnspecSingle@@QAEXXZ" to i8*), i32 0, i32 0, i32 0 }, align 8
 }
 
 namespace CastParam {
@@ -119,11 +119,11 @@ void (A::*ptr1)(void *) = (void (A::*)(v
 // Try a reinterpret_cast followed by a memptr conversion.
 void (C::*ptr2)(void *) = (void (C::*)(void *)) (void (A::*)(void *)) &A::foo;
 // CHECK: @"\01?ptr2 at CastParam@@3P8C at 1@AEXPAX at ZQ21@" =
-// CHECK:   global { i8*, i32 } { i8* bitcast (void ({{.*}})* @"\01?foo at A@CastParam@@QAEXPAU12@@Z" to i8*), i32 4 }, align 4
+// CHECK:   global { i8*, i32 } { i8* bitcast (void ({{.*}})* @"\01?foo at A@CastParam@@QAEXPAU12@@Z" to i8*), i32 4 }, align 8
 
 void (C::*ptr3)(void *) = (void (C::*)(void *)) (void (A::*)(void *)) (void (A::*)(A *)) 0;
 // CHECK: @"\01?ptr3 at CastParam@@3P8C at 1@AEXPAX at ZQ21@" =
-// CHECK:   global { i8*, i32 } zeroinitializer, align 4
+// CHECK:   global { i8*, i32 } zeroinitializer, align 8
 
 struct D : C {
   virtual void isPolymorphic();
@@ -156,23 +156,23 @@ void EmitNonVirtualMemberPointers() {
   void (UnspecWithVBPtr::*u2_f_memptr)() = &UnspecWithVBPtr::foo;
 // CHECK: define void @"\01?EmitNonVirtualMemberPointers@@YAXXZ"() {{.*}} {
 // CHECK:   alloca i8*, align 4
-// CHECK:   alloca { i8*, i32 }, align 4
-// CHECK:   alloca { i8*, i32, i32 }, align 4
-// CHECK:   alloca { i8*, i32, i32, i32 }, align 4
+// CHECK:   alloca { i8*, i32 }, align 8
+// CHECK:   alloca { i8*, i32, i32 }, align 8
+// CHECK:   alloca { i8*, i32, i32, i32 }, align 8
 // CHECK:   store i8* bitcast (void (%{{.*}}*)* @"\01?foo at Single@@QAEXXZ" to i8*), i8** %{{.*}}, align 4
 // CHECK:   store { i8*, i32 }
 // CHECK:     { i8* bitcast (void (%{{.*}}*)* @"\01?foo at Multiple@@QAEXXZ" to i8*), i32 0 },
-// CHECK:     { i8*, i32 }* %{{.*}}, align 4
+// CHECK:     { i8*, i32 }* %{{.*}}, align 8
 // CHECK:   store { i8*, i32, i32 }
 // CHECK:     { i8* bitcast (void (%{{.*}}*)* @"\01?foo at Virtual@@QAEXXZ" to i8*), i32 0, i32 0 },
-// CHECK:     { i8*, i32, i32 }* %{{.*}}, align 4
+// CHECK:     { i8*, i32, i32 }* %{{.*}}, align 8
 // CHECK:   store { i8*, i32, i32, i32 }
 // CHECK:     { i8* bitcast (void (%{{.*}}*)* @"\01?foo at Unspecified@@QAEXXZ" to i8*), i32 0, i32 12, i32 0 },
-// CHECK:     { i8*, i32, i32, i32 }* %{{.*}}, align 4
+// CHECK:     { i8*, i32, i32, i32 }* %{{.*}}, align 8
 // CHECK:   store { i8*, i32, i32, i32 }
 // CHECK:     { i8* bitcast (void (%{{.*}}*)* @"\01?foo at UnspecWithVBPtr@@QAEXXZ" to i8*),
 // CHECK:       i32 0, i32 4, i32 0 },
-// CHECK:     { i8*, i32, i32, i32 }* %{{.*}}, align 4
+// CHECK:     { i8*, i32, i32, i32 }* %{{.*}}, align 8
 // CHECK:   ret void
 // CHECK: }
 }
@@ -219,9 +219,9 @@ void polymorphicMemPtrs() {
 bool nullTestDataUnspecified(int Unspecified::*mp) {
   return mp;
 // CHECK: define zeroext i1 @"\01?nullTestDataUnspecified@@YA_NPQUnspecified@@H at Z"{{.*}} {
-// CHECK:   %{{.*}} = load { i32, i32, i32 }* %{{.*}}, align 4
-// CHECK:   store { i32, i32, i32 } {{.*}} align 4
-// CHECK:   %[[mp:.*]] = load { i32, i32, i32 }* %{{.*}}, align 4
+// CHECK:   %{{.*}} = load { i32, i32, i32 }* %{{.*}}, align 8
+// CHECK:   store { i32, i32, i32 } {{.*}} align 8
+// CHECK:   %[[mp:.*]] = load { i32, i32, i32 }* %{{.*}}, align 8
 // CHECK:   %[[mp0:.*]] = extractvalue { i32, i32, i32 } %[[mp]], 0
 // CHECK:   %[[cmp0:.*]] = icmp ne i32 %[[mp0]], 0
 // CHECK:   %[[mp1:.*]] = extractvalue { i32, i32, i32 } %[[mp]], 1
@@ -237,9 +237,9 @@ bool nullTestDataUnspecified(int Unspeci
 bool nullTestFunctionUnspecified(void (Unspecified::*mp)()) {
   return mp;
 // CHECK: define zeroext i1 @"\01?nullTestFunctionUnspecified@@YA_NP8Unspecified@@AEXXZ at Z"{{.*}} {
-// CHECK:   %{{.*}} = load { i8*, i32, i32, i32 }* %{{.*}}, align 4
-// CHECK:   store { i8*, i32, i32, i32 } {{.*}} align 4
-// CHECK:   %[[mp:.*]] = load { i8*, i32, i32, i32 }* %{{.*}}, align 4
+// CHECK:   %{{.*}} = load { i8*, i32, i32, i32 }* %{{.*}}, align 8
+// CHECK:   store { i8*, i32, i32, i32 } {{.*}} align 8
+// CHECK:   %[[mp:.*]] = load { i8*, i32, i32, i32 }* %{{.*}}, align 8
 // CHECK:   %[[mp0:.*]] = extractvalue { i8*, i32, i32, i32 } %[[mp]], 0
 // CHECK:   %[[cmp0:.*]] = icmp ne i8* %[[mp0]], null
 // CHECK:   ret i1 %[[cmp0]]
@@ -252,7 +252,7 @@ int loadDataMemberPointerVirtual(Virtual
 // data pointer.
 // CHECK: define i32 @"\01?loadDataMemberPointerVirtual@@YAHPAUVirtual@@PQ1 at H@Z"{{.*}} {
 // CHECK:   %[[o:.*]] = load %{{.*}}** %{{.*}}, align 4
-// CHECK:   %[[memptr:.*]] = load { i32, i32 }* %{{.*}}, align 4
+// CHECK:   %[[memptr:.*]] = load { i32, i32 }* %{{.*}}, align 8
 // CHECK:   %[[memptr0:.*]] = extractvalue { i32, i32 } %[[memptr:.*]], 0
 // CHECK:   %[[memptr1:.*]] = extractvalue { i32, i32 } %[[memptr:.*]], 1
 // CHECK:   %[[v6:.*]] = bitcast %{{.*}}* %[[o]] to i8*
@@ -276,7 +276,7 @@ int loadDataMemberPointerUnspecified(Uns
 // data pointer.
 // CHECK: define i32 @"\01?loadDataMemberPointerUnspecified@@YAHPAUUnspecified@@PQ1 at H@Z"{{.*}} {
 // CHECK:   %[[o:.*]] = load %{{.*}}** %{{.*}}, align 4
-// CHECK:   %[[memptr:.*]] = load { i32, i32, i32 }* %{{.*}}, align 4
+// CHECK:   %[[memptr:.*]] = load { i32, i32, i32 }* %{{.*}}, align 8
 // CHECK:   %[[memptr0:.*]] = extractvalue { i32, i32, i32 } %[[memptr:.*]], 0
 // CHECK:   %[[memptr1:.*]] = extractvalue { i32, i32, i32 } %[[memptr:.*]], 1
 // CHECK:   %[[memptr2:.*]] = extractvalue { i32, i32, i32 } %[[memptr:.*]], 2
@@ -462,7 +462,7 @@ void (B2::*convertMultipleFuncToB2(void
 //
 // CHECK: define i32 @"\01?convertMultipleFuncToB2@@YAP8B2@@AEXXZP8Multiple@@AEXXZ at Z"{{.*}} {
 // CHECK:   store
-// CHECK:   %[[src:.*]] = load { i8*, i32 }* %{{.*}}, align 4
+// CHECK:   %[[src:.*]] = load { i8*, i32 }* %{{.*}}, align 8
 // CHECK:   extractvalue { i8*, i32 } %[[src]], 0
 // CHECK:   icmp ne i8* %{{.*}}, null
 // CHECK:   br i1 %{{.*}}, label %{{.*}}, label %{{.*}}
@@ -487,7 +487,7 @@ void (D::*convertCToD(void (C::*mp)()))(
   return mp;
 // CHECK: define void @"\01?convertCToD at Test1@@YAP8D at 1@AEXXZP8C at 1@AEXXZ at Z"{{.*}} {
 // CHECK:   store
-// CHECK:   load { i8*, i32, i32 }* %{{.*}}, align 4
+// CHECK:   load { i8*, i32, i32 }* %{{.*}}, align 8
 // CHECK:   extractvalue { i8*, i32, i32 } %{{.*}}, 0
 // CHECK:   icmp ne i8* %{{.*}}, null
 // CHECK:   br i1 %{{.*}}, label %{{.*}}, label %{{.*}}

Added: cfe/trunk/test/Layout/ms-x86-member-pointers.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Layout/ms-x86-member-pointers.cpp?rev=200585&view=auto
==============================================================================
--- cfe/trunk/test/Layout/ms-x86-member-pointers.cpp (added)
+++ cfe/trunk/test/Layout/ms-x86-member-pointers.cpp Fri Jan 31 16:28:50 2014
@@ -0,0 +1,80 @@
+// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -triple i686-pc-win32 -fdump-record-layouts -fms-extensions -fsyntax-only %s 2>&1 | FileCheck %s
+
+struct __single_inheritance S;
+struct __multiple_inheritance M;
+struct __virtual_inheritance V;
+struct U;
+
+struct SD { char a; int S::*mp; };
+struct MD { char a; int M::*mp; };
+struct VD { char a; int V::*mp; };
+struct UD { char a; int U::*mp; };
+struct SF { char a; int (S::*mp)(); };
+struct MF { char a; int (M::*mp)(); };
+struct VF { char a; int (V::*mp)(); };
+struct UF { char a; int (U::*mp)(); };
+
+// CHECK: *** Dumping AST Record Layout
+// CHECK-NEXT:    0 | struct SD
+// CHECK-NEXT:    0 |   char a
+// CHECK-NEXT:    4 |   int struct S::* mp
+// CHECK-NEXT:      | [sizeof=8, align=4
+// CHECK-NEXT:      |  nvsize=8, nvalign=4]
+
+// CHECK: *** Dumping AST Record Layout
+// CHECK-NEXT:    0 | struct MD
+// CHECK-NEXT:    0 |   char a
+// CHECK-NEXT:    4 |   int struct M::* mp
+// CHECK-NEXT:      | [sizeof=8, align=4
+// CHECK-NEXT:      |  nvsize=8, nvalign=4]
+
+// CHECK: *** Dumping AST Record Layout
+// CHECK-NEXT:    0 | struct VD
+// CHECK-NEXT:    0 |   char a
+// CHECK-NEXT:    8 |   int struct V::* mp
+// CHECK-NEXT:      | [sizeof=16, align=8
+// CHECK-NEXT:      |  nvsize=16, nvalign=8]
+
+// CHECK: *** Dumping AST Record Layout
+// CHECK-NEXT:    0 | struct UD
+// CHECK-NEXT:    0 |   char a
+// CHECK-NEXT:    8 |   int struct U::* mp
+// CHECK-NEXT:      | [sizeof=24, align=8
+// CHECK-NEXT:      |  nvsize=24, nvalign=8]
+
+// CHECK: *** Dumping AST Record Layout
+// CHECK-NEXT:    0 | struct SF
+// CHECK-NEXT:    0 |   char a
+// CHECK-NEXT:    4 |   int (struct S::*)(void) __attribute__((thiscall)) mp
+// CHECK-NEXT:      | [sizeof=8, align=4
+// CHECK-NEXT:      |  nvsize=8, nvalign=4]
+
+// CHECK: *** Dumping AST Record Layout
+// CHECK-NEXT:    0 | struct MF
+// CHECK-NEXT:    0 |   char a
+// CHECK-NEXT:    8 |   int (struct M::*)(void) __attribute__((thiscall)) mp
+// CHECK-NEXT:      | [sizeof=16, align=8
+// CHECK-NEXT:      |  nvsize=16, nvalign=8]
+
+// CHECK: *** Dumping AST Record Layout
+// CHECK-NEXT:    0 | struct VF
+// CHECK-NEXT:    0 |   char a
+// CHECK-NEXT:    8 |   int (struct V::*)(void) __attribute__((thiscall)) mp
+// CHECK-NEXT:      | [sizeof=24, align=8
+// CHECK-NEXT:      |  nvsize=24, nvalign=8]
+
+// CHECK: *** Dumping AST Record Layout
+// CHECK-NEXT:    0 | struct UF
+// CHECK-NEXT:    0 |   char a
+// CHECK-NEXT:    8 |   int (struct U::*)(void) __attribute__((thiscall)) mp
+// CHECK-NEXT:      | [sizeof=24, align=8
+// CHECK-NEXT:      |  nvsize=24, nvalign=8]
+
+char a[sizeof(SD) +
+       sizeof(MD) +
+       sizeof(VD) +
+       sizeof(UD) +
+       sizeof(SF) +
+       sizeof(MF) +
+       sizeof(VF) +
+       sizeof(UF)];

Modified: cfe/trunk/test/SemaCXX/member-pointer-ms.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/member-pointer-ms.cpp?rev=200585&r1=200584&r2=200585&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/member-pointer-ms.cpp (original)
+++ cfe/trunk/test/SemaCXX/member-pointer-ms.cpp Fri Jan 31 16:28:50 2014
@@ -19,15 +19,29 @@ struct Foo {
 };
 
 enum {
+  kSingleDataAlign             = 1 * sizeof(int),
+  kSingleFunctionAlign         = 1 * sizeof(void *),
+  kMultipleDataAlign           = 1 * sizeof(int),
+  // Everything with more than 1 field is 8 byte aligned, except virtual data
+  // member pointers on x64 (ugh).
+  kMultipleFunctionAlign       = 8,
+#ifdef _M_X64
+  kVirtualDataAlign            = 4,
+#else
+  kVirtualDataAlign            = 8,
+#endif
+  kVirtualFunctionAlign        = 8,
+  kUnspecifiedDataAlign        = 8,
+  kUnspecifiedFunctionAlign    = 8,
+
   kSingleDataSize             = 1 * sizeof(int),
   kSingleFunctionSize         = 1 * sizeof(void *),
   kMultipleDataSize           = 1 * sizeof(int),
   kMultipleFunctionSize       = 2 * sizeof(void *),
   kVirtualDataSize            = 2 * sizeof(int),
   kVirtualFunctionSize        = 2 * sizeof(int) + 1 * sizeof(void *),
-  // Unspecified is weird, it's 1 more slot than virtual.
-  kUnspecifiedDataSize        = kVirtualDataSize + 1 * sizeof(int),
-  kUnspecifiedFunctionSize    = kVirtualFunctionSize + 1 * sizeof(void *),
+  kUnspecifiedDataSize        = 3 * sizeof(int),
+  kUnspecifiedFunctionSize    = 2 * sizeof(int) + 2 * sizeof(void *),
 };
 
 // incomplete types
@@ -41,6 +55,13 @@ static_assert(sizeof(void (IncSingle::*)
 static_assert(sizeof(void (IncMultiple::*)()) == kMultipleFunctionSize, "");
 static_assert(sizeof(void (IncVirtual::*)())  == kVirtualFunctionSize, "");
 
+static_assert(__alignof(int IncSingle::*)        == kSingleDataAlign, "");
+static_assert(__alignof(int IncMultiple::*)      == kMultipleDataAlign, "");
+static_assert(__alignof(int IncVirtual::*)       == kVirtualDataAlign, "");
+static_assert(__alignof(void (IncSingle::*)())   == kSingleFunctionAlign, "");
+static_assert(__alignof(void (IncMultiple::*)()) == kMultipleFunctionAlign, "");
+static_assert(__alignof(void (IncVirtual::*)())  == kVirtualFunctionAlign, "");
+
 // An incomplete type with an unspecified inheritance model seems to take one
 // more slot than virtual.  It's not clear what it's used for yet.
 class IncUnspecified;





More information about the cfe-commits mailing list