r203222 - Fix PR18967 -- Bad this adjustment for virtual methods in a diamond virtual inheritance hierarchy

Timur Iskhodzhanov timurrrr at google.com
Fri Mar 7 01:35:00 PST 2014


Author: timurrrr
Date: Fri Mar  7 03:34:59 2014
New Revision: 203222

URL: http://llvm.org/viewvc/llvm-project?rev=203222&view=rev
Log:
Fix PR18967 -- Bad this adjustment for virtual methods in a diamond virtual inheritance hierarchy

Modified:
    cfe/trunk/lib/AST/VTableBuilder.cpp
    cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp
    cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp
    cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-return-thunks.cpp
    cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp
    cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp

Modified: cfe/trunk/lib/AST/VTableBuilder.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/VTableBuilder.cpp?rev=203222&r1=203221&r2=203222&view=diff
==============================================================================
--- cfe/trunk/lib/AST/VTableBuilder.cpp (original)
+++ cfe/trunk/lib/AST/VTableBuilder.cpp Fri Mar  7 03:34:59 2014
@@ -2520,13 +2520,15 @@ private:
   /// AddMethod - Add a single virtual member function to the vftable
   /// components vector.
   void AddMethod(const CXXMethodDecl *MD, ThunkInfo TI) {
+    if (!TI.isEmpty()) {
+      VTableThunks[Components.size()] = TI;
+      AddThunk(MD, TI);
+    }
     if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
       assert(TI.Return.isEmpty() &&
              "Destructor can't have return adjustment!");
       Components.push_back(VTableComponent::MakeDeletingDtor(DD));
     } else {
-      if (!TI.isEmpty())
-        VTableThunks[Components.size()] = TI;
       Components.push_back(VTableComponent::MakeFunction(MD));
     }
   }
@@ -2539,47 +2541,10 @@ private:
                   const CXXRecordDecl *LastVBase,
                   BasesSetVectorTy &VisitedBases);
 
-  void CheckBadVirtualInheritanceHierarchy() {
-    // We fail at this-adjustment for virtual methods inherited from
-    // non-virtual bases that overrides a method in a virtual base.
-    if (Context.getLangOpts().DumpVTableLayouts)
-      return;
-    for (CXXRecordDecl::base_class_const_iterator BI =
-        MostDerivedClass->bases_begin(), BE = MostDerivedClass->bases_end();
-        BI != BE; ++BI) {
-      const CXXRecordDecl *Base = BI->getType()->getAsCXXRecordDecl();
-      if (BI->isVirtual())
-        continue;
-      for (CXXRecordDecl::method_iterator I = Base->method_begin(),
-          E = Base->method_end(); I != E; ++I) {
-        const CXXMethodDecl *Method = *I;
-        if (!Method->isVirtual())
-          continue;
-        if (isa<CXXDestructorDecl>(Method))
-          continue;
-        OverriddenMethodsSetTy OverriddenMethods;
-        ComputeAllOverriddenMethods(Method, OverriddenMethods);
-        for (OverriddenMethodsSetTy::const_iterator I =
-            OverriddenMethods.begin(),
-            E = OverriddenMethods.end(); I != E; ++I) {
-          const CXXMethodDecl *Overridden = *I;
-          if (Base->isVirtuallyDerivedFrom(Overridden->getParent())) {
-            ErrorUnsupported("classes with non-virtual base "
-                "classes that override methods in virtual bases",
-                BI->getLocStart());
-            return;
-          }
-        }
-      }
-    }
-  }
-
   void LayoutVFTable() {
     // FIXME: add support for RTTI when we have proper LLVM support for symbols
     // pointing to the middle of a section.
 
-    CheckBadVirtualInheritanceHierarchy();
-
     BasesSetVectorTy VisitedBases;
     AddMethods(BaseSubobject(MostDerivedClass, CharUnits::Zero()), 0, 0,
                VisitedBases);
@@ -2687,6 +2652,11 @@ VFTableBuilder::ComputeThisOffset(FinalO
   InitialOverriddenDefinitionCollector Collector;
   visitAllOverriddenMethods(Overrider.Method, Collector);
 
+  // If there are no overrides then 'this' is located
+  // in the base that defines the method.
+  if (Collector.Bases.size() == 0)
+    return Overrider.Offset;
+
   CXXBasePaths Paths;
   Overrider.Method->getParent()->lookupInBases(BaseInSet, &Collector.Bases,
                                                Paths);
@@ -2899,13 +2869,21 @@ void VFTableBuilder::AddMethods(BaseSubo
 
     FinalOverriders::OverriderInfo Overrider =
         Overriders.getOverrider(MD, Base.getBaseOffset());
+    const CXXMethodDecl *OverriderMD = Overrider.Method;
+    const CXXMethodDecl *OverriddenMD =
+        FindNearestOverriddenMethod(MD, VisitedBases);
+
     ThisAdjustment ThisAdjustmentOffset;
-    bool ForceThunk = false;
+    bool ReturnAdjustingThunk = false;
+    CharUnits ThisOffset = ComputeThisOffset(Overrider);
+    ThisAdjustmentOffset.NonVirtual =
+        (ThisOffset - WhichVFPtr.FullOffsetInMDC).getQuantity();
+    if ((OverriddenMD || OverriderMD != MD) &&
+        WhichVFPtr.getVBaseWithVPtr())
+      CalculateVtordispAdjustment(Overrider, ThisOffset, ThisAdjustmentOffset);
 
-    // Check if this virtual member function overrides
-    // a method in one of the visited bases.
-    if (const CXXMethodDecl *OverriddenMD =
-            FindNearestOverriddenMethod(MD, VisitedBases)) {
+    if (OverriddenMD) {
+      // If MD overrides anything in this vftable, we need to update the entries.
       MethodInfoMapTy::iterator OverriddenMDIterator =
           MethodInfoMap.find(OverriddenMD);
 
@@ -2915,22 +2893,6 @@ void VFTableBuilder::AddMethods(BaseSubo
 
       MethodInfo &OverriddenMethodInfo = OverriddenMDIterator->second;
 
-      // Create a this-adjusting thunk if needed.
-      CharUnits TI = ComputeThisOffset(Overrider);
-      if (TI != WhichVFPtr.FullOffsetInMDC) {
-        ThisAdjustmentOffset.NonVirtual =
-            (TI - WhichVFPtr.FullOffsetInMDC).getQuantity();
-      }
-
-      if (WhichVFPtr.getVBaseWithVPtr())
-        CalculateVtordispAdjustment(Overrider, TI, ThisAdjustmentOffset);
-
-      if (!ThisAdjustmentOffset.isEmpty()) {
-        VTableThunks[OverriddenMethodInfo.VFTableIndex].This =
-            ThisAdjustmentOffset;
-        AddThunk(MD, VTableThunks[OverriddenMethodInfo.VFTableIndex]);
-      }
-
       if (!NeedsReturnAdjustingThunk(MD)) {
         // No return adjustment needed - just replace the overridden method info
         // with the current info.
@@ -2945,29 +2907,13 @@ void VFTableBuilder::AddMethods(BaseSubo
       }
 
       // In case we need a return adjustment, we'll add a new slot for
-      // the overrider and put a return-adjusting thunk where the overridden
-      // method was in the vftable.
-      // For now, just mark the overriden method as shadowed by a new slot.
+      // the overrider. Mark the overriden method as shadowed by the new slot.
       OverriddenMethodInfo.Shadowed = true;
-      ForceThunk = true;
 
-      // Also apply this adjustment to the shadowed slots.
-      if (!ThisAdjustmentOffset.isEmpty()) {
-        // FIXME: this is O(N^2), can be O(N).
-        const CXXMethodDecl *SubOverride = OverriddenMD;
-        while ((SubOverride =
-                    FindNearestOverriddenMethod(SubOverride, VisitedBases))) {
-          MethodInfoMapTy::iterator SubOverrideIterator =
-              MethodInfoMap.find(SubOverride);
-          if (SubOverrideIterator == MethodInfoMap.end())
-            break;
-          MethodInfo &SubOverrideMI = SubOverrideIterator->second;
-          assert(SubOverrideMI.Shadowed);
-          VTableThunks[SubOverrideMI.VFTableIndex].This =
-              ThisAdjustmentOffset;
-          AddThunk(MD, VTableThunks[SubOverrideMI.VFTableIndex]);
-        }
-      }
+      // Force a special name mangling for a return-adjusting thunk
+      // unless the method is the final overrider without this adjustment.
+      ReturnAdjustingThunk =
+          !(MD == OverriderMD && ThisAdjustmentOffset.isEmpty());
     } else if (Base.getBaseOffset() != WhichVFPtr.FullOffsetInMDC ||
                MD->size_overridden_methods()) {
       // Skip methods that don't belong to the vftable of the current class,
@@ -2986,8 +2932,6 @@ void VFTableBuilder::AddMethods(BaseSubo
            "Should not have method info for this method yet!");
     MethodInfoMap.insert(std::make_pair(MD, MI));
 
-    const CXXMethodDecl *OverriderMD = Overrider.Method;
-
     // Check if this overrider needs a return adjustment.
     // We don't want to do this for pure virtual member functions.
     BaseOffset ReturnAdjustmentOffset;
@@ -2997,7 +2941,7 @@ void VFTableBuilder::AddMethods(BaseSubo
           ComputeReturnAdjustmentBaseOffset(Context, OverriderMD, MD);
     }
     if (!ReturnAdjustmentOffset.isEmpty()) {
-      ForceThunk = true;
+      ReturnAdjustingThunk = true;
       ReturnAdjustment.NonVirtual =
           ReturnAdjustmentOffset.NonVirtualOffset.getQuantity();
       if (ReturnAdjustmentOffset.VirtualBase) {
@@ -3012,7 +2956,7 @@ void VFTableBuilder::AddMethods(BaseSubo
     }
 
     AddMethod(OverriderMD, ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment,
-                                     ForceThunk ? MD : 0));
+                                     ReturnAdjustingThunk ? MD : 0));
   }
 }
 
@@ -3029,11 +2973,13 @@ static void dumpMicrosoftThunkAdjustment
                                          bool ContinueFirstLine) {
   const ReturnAdjustment &R = TI.Return;
   bool Multiline = false;
-  const char *LinePrefix = "\n        ";
-  if (!R.isEmpty()) {
+  const char *LinePrefix = "\n       ";
+  if (!R.isEmpty() || TI.Method) {
     if (!ContinueFirstLine)
       Out << LinePrefix;
-    Out << "[return adjustment: ";
+    Out << "[return adjustment (to type '"
+        << TI.Method->getReturnType().getCanonicalType().getAsString()
+        << "'): ";
     if (R.Virtual.Microsoft.VBPtrOffset)
       Out << "vbptr at offset " << R.Virtual.Microsoft.VBPtrOffset << ", ";
     if (R.Virtual.Microsoft.VBIndex)
@@ -3052,7 +2998,7 @@ static void dumpMicrosoftThunkAdjustment
       Out << "vtordisp at " << T.Virtual.Microsoft.VtordispOffset << ", ";
       if (T.Virtual.Microsoft.VBPtrOffset) {
         Out << "vbptr at " << T.Virtual.Microsoft.VBPtrOffset
-            << " to the left, ";
+            << " to the left,";
         assert(T.Virtual.Microsoft.VBOffsetOffset > 0);
         Out << LinePrefix << " vboffset at "
             << T.Virtual.Microsoft.VBOffsetOffset << " in the vbtable, ";

Modified: cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp?rev=203222&r1=203221&r2=203222&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp Fri Mar  7 03:34:59 2014
@@ -217,12 +217,7 @@ void call_complete_dtor() {
   // CHECK: ret
 }
 
-struct X : virtual VBase {
-  int x;
-};
-
-
-struct C : X {
+struct C : B {
   C();
   // has an implicit vdtor.
 };

Modified: cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp?rev=203222&r1=203221&r2=203222&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp Fri Mar  7 03:34:59 2014
@@ -458,10 +458,13 @@ struct Ret1 {
 struct Test1 : Ret1 {
   // RET-THUNKS-Test1: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' (3 entries).
   // RET-THUNKS-Test1-NEXT: 0 | this_adjustment::Test1 *return_adjustment::Test1::foo()
-  // RET-THUNKS-Test1-NEXT:     [return adjustment: 4 non-virtual]
+  // RET-THUNKS-Test1-NEXT:     [return adjustment (to type 'struct C *'): 4 non-virtual]
   // RET-THUNKS-Test1-NEXT: 1 | void return_adjustment::Ret1::z()
   // RET-THUNKS-Test1-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test1::foo()
 
+  // RET-THUNKS-Test1: Thunks for 'this_adjustment::Test1 *return_adjustment::Test1::foo()' (1 entry).
+  // RET-THUNKS-Test1-NEXT: 0 | [return adjustment (to type 'struct C *'): 4 non-virtual]
+
   // RET-THUNKS-Test1: VFTable indices for 'return_adjustment::Test1' (1 entry).
   // RET-THUNKS-Test1-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test1::foo()
 
@@ -477,12 +480,16 @@ struct Ret2 : B, this_adjustment::Test1
 struct Test2 : Test1 {
   // RET-THUNKS-Test2: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test2' (4 entries).
   // RET-THUNKS-Test2-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
-  // RET-THUNKS-Test2-NEXT:     [return adjustment: 8 non-virtual]
+  // RET-THUNKS-Test2-NEXT:     [return adjustment (to type 'struct C *'): 8 non-virtual]
   // RET-THUNKS-Test2-NEXT: 1 | void return_adjustment::Ret1::z()
   // RET-THUNKS-Test2-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
-  // RET-THUNKS-Test2-NEXT:     [return adjustment: 4 non-virtual]
+  // RET-THUNKS-Test2-NEXT:     [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual]
   // RET-THUNKS-Test2-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
 
+  // RET-THUNKS-Test2: Thunks for 'return_adjustment::Ret2 *return_adjustment::Test2::foo()' (2 entries).
+  // RET-THUNKS-Test2-NEXT: 0 | [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual]
+  // RET-THUNKS-Test2-NEXT: 1 | [return adjustment (to type 'struct C *'): 8 non-virtual]
+
   // RET-THUNKS-Test2: VFTable indices for 'return_adjustment::Test2' (1 entry).
   // RET-THUNKS-Test2-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
 
@@ -498,10 +505,13 @@ struct Test3: B, Ret1 {
 
   // RET-THUNKS-Test3: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test3' (3 entries).
   // RET-THUNKS-Test3-NEXT: 0 | this_adjustment::Test1 *return_adjustment::Test3::foo()
-  // RET-THUNKS-Test3-NEXT:     [return adjustment: 4 non-virtual]
+  // RET-THUNKS-Test3-NEXT:     [return adjustment (to type 'struct C *'): 4 non-virtual]
   // RET-THUNKS-Test3-NEXT: 1 | void return_adjustment::Ret1::z()
   // RET-THUNKS-Test3-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test3::foo()
 
+  // RET-THUNKS-Test3: Thunks for 'this_adjustment::Test1 *return_adjustment::Test3::foo()' (1 entry).
+  // RET-THUNKS-Test3-NEXT: 0 | [return adjustment (to type 'struct C *'): 4 non-virtual]
+
   // RET-THUNKS-Test3: VFTable indices for 'return_adjustment::Test3' (1 entry).
   // RET-THUNKS-Test3-NEXT: via vfptr at offset 4
   // RET-THUNKS-Test3-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test3::foo()
@@ -518,15 +528,19 @@ struct Test4 : Test3 {
 
   // RET-THUNKS-Test4: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test3' in 'return_adjustment::Test4' (4 entries).
   // RET-THUNKS-Test4-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
-  // RET-THUNKS-Test4-NEXT:     [return adjustment: 8 non-virtual]
+  // RET-THUNKS-Test4-NEXT:     [return adjustment (to type 'struct C *'): 8 non-virtual]
   // RET-THUNKS-Test4-NEXT: 1 | void return_adjustment::Ret1::z()
   // RET-THUNKS-Test4-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
-  // RET-THUNKS-Test4-NEXT:     [return adjustment: 4 non-virtual]
+  // RET-THUNKS-Test4-NEXT:     [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual]
   // RET-THUNKS-Test4-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
 
+  // RET-THUNKS-Test4: Thunks for 'return_adjustment::Ret2 *return_adjustment::Test4::foo()' (2 entries).
+  // RET-THUNKS-Test4-NEXT: 0 | [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual]
+  // RET-THUNKS-Test4-NEXT: 1 | [return adjustment (to type 'struct C *'): 8 non-virtual]
+
   // RET-THUNKS-Test4: VFTable indices for 'return_adjustment::Test4' (1 entry).
   // RET-THUNKS-Test4-NEXT: -- accessible via vfptr at offset 4 --
-  // RET-THUNKS-Test4-NEXT:   3 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
+  // RET-THUNKS-Test4-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
 
   virtual Ret2* foo();
 };
@@ -536,19 +550,31 @@ Test4 t4;
 struct Test5 : Ret1, Test1 {
   // RET-THUNKS-Test5: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test5' (3 entries).
   // RET-THUNKS-Test5-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
-  // RET-THUNKS-Test5-NEXT:     [return adjustment: 8 non-virtual]
+  // RET-THUNKS-Test5-NEXT:     [return adjustment (to type 'struct C *'): 8 non-virtual]
   // RET-THUNKS-Test5-NEXT: 1 | void return_adjustment::Ret1::z()
   // RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
 
+  // RET-THUNKS-Test5: Thunks for 'return_adjustment::Ret2 *return_adjustment::Test5::foo()' (1 entry).
+  // RET-THUNKS-Test5-NEXT: 0 | [return adjustment (to type 'struct C *'): 8 non-virtual]
+
   // RET-THUNKS-Test5: VFTable for 'return_adjustment::Ret1' in  'return_adjustment::Test1' in 'return_adjustment::Test5' (4 entries).
   // RET-THUNKS-Test5-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
-  // RET-THUNKS-Test5-NEXT:     [return adjustment: 8 non-virtual]
+  // RET-THUNKS-Test5-NEXT:     [return adjustment (to type 'struct C *'): 8 non-virtual]
   // RET-THUNKS-Test5-NEXT:     [this adjustment: -4 non-virtual]
   // RET-THUNKS-Test5-NEXT: 1 | void return_adjustment::Ret1::z()
   // RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
-  // RET-THUNKS-Test5-NEXT:     [return adjustment: 4 non-virtual]
+  // RET-THUNKS-Test5-NEXT:     [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual]
   // RET-THUNKS-Test5-NEXT:     [this adjustment: -4 non-virtual]
   // RET-THUNKS-Test5-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
+  // RET-THUNKS-Test5-NEXT:     [return adjustment (to type 'struct return_adjustment::Ret2 *'): 0 non-virtual]
+  // RET-THUNKS-Test5-NEXT:     [this adjustment: -4 non-virtual]
+
+  // RET-THUNKS-Test5: Thunks for 'return_adjustment::Ret2 *return_adjustment::Test5::foo()' (3 entries).
+  // RET-THUNKS-Test5-NEXT: 0 | [return adjustment (to type 'struct return_adjustment::Ret2 *'): 0 non-virtual]
+  // RET-THUNKS-Test5-NEXT:     [this adjustment: -4 non-virtual]
+  // RET-THUNKS-Test5-NEXT: 1 | [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual]
+  // RET-THUNKS-Test5-NEXT:     [this adjustment: -4 non-virtual]
+  // RET-THUNKS-Test5-NEXT: 2 | [return adjustment (to type 'struct C *'): 8 non-virtual]
   // RET-THUNKS-Test5-NEXT:     [this adjustment: -4 non-virtual]
 
   // RET-THUNKS-Test5: VFTable indices for 'return_adjustment::Test5' (1 entry).
@@ -565,11 +591,16 @@ struct Test6 : Test1 {
   virtual Ret3* foo();
   // RET-THUNKS-Test6: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test6' (4 entries).
   // RET-THUNKS-Test6-NEXT: 0 | return_adjustment::Ret3 *return_adjustment::Test6::foo()
-  // RET-THUNKS-Test6-NEXT:     [return adjustment: 4 non-virtual]
+  // RET-THUNKS-Test6-NEXT:     [return adjustment (to type 'struct C *'): 4 non-virtual]
   // RET-THUNKS-Test6-NEXT: 1 | void return_adjustment::Ret1::z()
   // RET-THUNKS-Test6-NEXT: 2 | return_adjustment::Ret3 *return_adjustment::Test6::foo()
+  // RET-THUNKS-Test6-NEXT:     [return adjustment (to type 'struct this_adjustment::Test1 *'): 0 non-virtual]
   // RET-THUNKS-Test6-NEXT: 3 | return_adjustment::Ret3 *return_adjustment::Test6::foo()
 
+  // RET-THUNKS-Test6: Thunks for 'return_adjustment::Ret3 *return_adjustment::Test6::foo()' (2 entries).
+  // RET-THUNKS-Test6-NEXT: 0 | [return adjustment (to type 'struct this_adjustment::Test1 *'): 0 non-virtual]
+  // RET-THUNKS-Test6-NEXT: 1 | [return adjustment (to type 'struct C *'): 4 non-virtual]
+
   // RET-THUNKS-Test6: VFTable indices for 'return_adjustment::Test6' (1 entry).
   // RET-THUNKS-Test6-NEXT: 3 | return_adjustment::Ret3 *return_adjustment::Test6::foo()
 };

Modified: cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-return-thunks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-return-thunks.cpp?rev=203222&r1=203221&r2=203222&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-return-thunks.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-return-thunks.cpp Fri Mar  7 03:34:59 2014
@@ -20,25 +20,27 @@ struct K : J { virtual E *foo(); K(); };
 J::J() {}
 
 // VFTABLES-LABEL: VFTable for 'test1::H' in 'test1::I' in 'test1::J' (3 entries).
-// VFTABLES:    0 | test1::D *test1::J::foo()
-// VFTABLES:         [return adjustment: 4 non-virtual]
-// VFTABLES:    1 | test1::D *test1::J::foo()
-// VFTABLES:    2 | test1::D *test1::J::foo()
+// VFTABLES-NEXT:   0 | test1::D *test1::J::foo()
+// VFTABLES-NEXT:       [return adjustment (to type 'struct test1::B *'): 4 non-virtual]
+// VFTABLES-NEXT:   1 | test1::D *test1::J::foo()
+// VFTABLES-NEXT:       [return adjustment (to type 'struct test1::C *'): 0 non-virtual]
+// VFTABLES-NEXT:   2 | test1::D *test1::J::foo()
 
 // GLOBALS-LABEL: @"\01??_7J at test1@@6B@" = linkonce_odr unnamed_addr constant [3 x i8*]
 // GLOBALS: @"\01?foo at J@test1@@QAEPAUB at 2@XZ"
 // GLOBALS: @"\01?foo at J@test1@@QAEPAUC at 2@XZ"
-// GLOBALS: @"\01?foo at J@test1@@QAEPAUD at 2@XZ"
-// FIXME: Should be UAEPAUD.
+// GLOBALS: @"\01?foo at J@test1@@UAEPAUD at 2@XZ"
 
 K::K() {}
 
 // VFTABLES-LABEL: VFTable for 'test1::H' in 'test1::I' in 'test1::J' in 'test1::K' (4 entries).
-// VFTABLES:   0 | test1::E *test1::K::foo()
-// VFTABLES:        [return adjustment: 4 non-virtual]
-// VFTABLES:   1 | test1::E *test1::K::foo()
-// VFTABLES:   2 | test1::E *test1::K::foo()
-// VFTABLES:   3 | test1::E *test1::K::foo()
+// VFTABLES-NEXT:   0 | test1::E *test1::K::foo()
+// VFTABLES-NEXT:       [return adjustment (to type 'struct test1::B *'): 4 non-virtual]
+// VFTABLES-NEXT:   1 | test1::E *test1::K::foo()
+// VFTABLES-NEXT:       [return adjustment (to type 'struct test1::C *'): 0 non-virtual]
+// VFTABLES-NEXT:   2 | test1::E *test1::K::foo()
+// VFTABLES-NEXT:       [return adjustment (to type 'struct test1::D *'): 0 non-virtual]
+// VFTABLES-NEXT:   3 | test1::E *test1::K::foo()
 
 // Only B to C requires adjustment, but we get 3 thunks in K's vftable, two of
 // which are trivial.
@@ -46,8 +48,7 @@ K::K() {}
 // GLOBALS: @"\01?foo at K@test1@@QAEPAUB at 2@XZ"
 // GLOBALS: @"\01?foo at K@test1@@QAEPAUC at 2@XZ"
 // GLOBALS: @"\01?foo at K@test1@@QAEPAUD at 2@XZ"
-// GLOBALS: @"\01?foo at K@test1@@QAEPAUE at 2@XZ"
-// FIXME: Should be UAEPAUE.
+// GLOBALS: @"\01?foo at K@test1@@UAEPAUE at 2@XZ"
 
 //  This thunk has a return adjustment.
 // CODEGEN-LABEL: define {{.*}} @"\01?foo at K@test1@@QAEPAUB at 2@XZ"
@@ -85,19 +86,20 @@ struct K : J { virtual E *foo(); K(); };
 J::J() {}
 
 // VFTABLES-LABEL: VFTable for 'test2::H' in 'test2::I' in 'test2::J' (2 entries).
-// VFTABLES:    0 | test2::D *test2::J::foo()
-// VFTABLES:         [return adjustment: 4 non-virtual]
-// VFTABLES:    1 | test2::D *test2::J::foo()
+// VFTABLES-NEXT:    0 | test2::D *test2::J::foo()
+// VFTABLES-NEXT:         [return adjustment (to type 'struct test2::B *'): 4 non-virtual]
+// VFTABLES-NEXT:    1 | test2::D *test2::J::foo()
 
 // GLOBALS-LABEL: @"\01??_7J at test2@@6B@" = linkonce_odr unnamed_addr constant [2 x i8*]
 
 K::K() {}
 
 // VFTABLES-LABEL: VFTable for 'test2::H' in 'test2::I' in 'test2::J' in 'test2::K' (3 entries).
-// VFTABLES:    0 | test2::E *test2::K::foo()
-// VFTABLES:         [return adjustment: 4 non-virtual]
-// VFTABLES:    1 | test2::E *test2::K::foo()
-// VFTABLES:    2 | test2::E *test2::K::foo()
+// VFTABLES-NEXT:    0 | test2::E *test2::K::foo()
+// VFTABLES-NEXT:         [return adjustment (to type 'struct test2::B *'): 4 non-virtual]
+// VFTABLES-NEXT:    1 | test2::E *test2::K::foo()
+// VFTABLES-NEXT:         [return adjustment (to type 'struct test2::D *'): 0 non-virtual]
+// VFTABLES-NEXT:    2 | test2::E *test2::K::foo()
 
 // GLOBALS-LABEL: @"\01??_7K at test2@@6B@" = linkonce_odr unnamed_addr constant [3 x i8*]
 

Modified: cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp?rev=203222&r1=203221&r2=203222&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp Fri Mar  7 03:34:59 2014
@@ -70,6 +70,12 @@ struct A : virtual V1 {
   // VTABLE-SIMPLE-A-NEXT: 1 | simple::A::~A() [scalar deleting]
   // VTABLE-SIMPLE-A-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
 
+  // VTABLE-SIMPLE-A: Thunks for 'simple::A::~A()' (1 entry).
+  // VTABLE-SIMPLE-A-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
+
+  // VTABLE-SIMPLE-A: Thunks for 'void simple::A::f()' (1 entry).
+  // VTABLE-SIMPLE-A-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
+
   virtual void f();
   // MANGLING-DAG: @"\01?f at A@simple@@$4PPPPPPPM at A@AEXXZ"
 
@@ -85,12 +91,21 @@ struct B : virtual V3 {
   // VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting]
   // VTABLE-SIMPLE-B-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
 
+  // VTABLE-SIMPLE-B: Thunks for 'simple::B::~B()' (1 entry).
+  // VTABLE-SIMPLE-B-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
+
   // VTABLE-SIMPLE-B: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries).
   // VTABLE-SIMPLE-B-NEXT: 0 | void simple::B::f()
   // VTABLE-SIMPLE-B-NEXT:     [this adjustment: vtordisp at -12, 0 non-virtual]
   // VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting]
   // VTABLE-SIMPLE-B-NEXT:     [this adjustment: vtordisp at -12, -8 non-virtual]
 
+  // VTABLE-SIMPLE-B: Thunks for 'simple::B::~B()' (1 entry).
+  // VTABLE-SIMPLE-B-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual]
+
+  // VTABLE-SIMPLE-B: Thunks for 'void simple::B::f()' (1 entry).
+  // VTABLE-SIMPLE-B-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual]
+
   // FIXME: The vtordisp thunk should only get emitted for a constructor
   // if "this" leaves scope.
   B() { use_somewhere_else(this); }
@@ -111,18 +126,33 @@ struct C : virtual V4 {
   // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
   // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
 
+  // VTABLE-SIMPLE-C: Thunks for 'simple::C::~C()' (1 entry).
+  // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
+
   // VTABLE-SIMPLE-C: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries).
   // VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f()
   // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -12, 0 non-virtual]
   // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
   // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -12, -8 non-virtual]
 
+  // VTABLE-SIMPLE-C: Thunks for 'simple::C::~C()' (1 entry).
+  // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual]
+
+  // VTABLE-SIMPLE-C: Thunks for 'void simple::C::f()' (1 entry).
+  // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual]
+
   // VTABLE-SIMPLE-C: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries).
   // VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f()
   // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -16, -4 non-virtual]
   // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
   // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -16, -12 non-virtual]
 
+  // VTABLE-SIMPLE-C: Thunks for 'simple::C::~C()' (1 entry).
+  // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -16, -12 non-virtual]
+
+  // VTABLE-SIMPLE-C: Thunks for 'void simple::C::f()' (1 entry).
+  // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -16, -4 non-virtual]
+
   int x;
   virtual void f();
   // MANGLING-DAG: @"\01?f at C@simple@@$4PPPPPPPA at 3AEXXZ"
@@ -159,6 +189,10 @@ struct A : virtual simple::A {
   // VTABLE-EXTENDED-A-NEXT: 1 | extended::A::~A() [scalar deleting]
   // VTABLE-EXTENDED-A-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
 
+  // VTABLE-EXTENDED-A: Thunks for 'void simple::A::f()' (1 entry).
+  // VTABLE-EXTENDED-A-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
+  // VTABLE-EXTENDED-A-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
+
   // `vtordispex{8,8,4294967292,8}'
   // MANGLING-DAG: @"\01?f at A@simple@@$R477PPPPPPPM at 7AEXXZ"
 
@@ -179,6 +213,10 @@ struct B : virtual simple::A {
   // VTABLE-EXTENDED-B: 1 | extended::B::~B() [scalar deleting]
   // VTABLE-EXTENDED-B-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
 
+  // VTABLE-EXTENDED-B: Thunks for 'void simple::A::f()' (1 entry).
+  // VTABLE-EXTENDED-B-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
+  // VTABLE-EXTENDED-B-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
+
   // vtordisp{4294967292,0}
   // MANGLING-DAG: @"\01??_EB at extended@@$4PPPPPPPM at A@AEPAXI at Z"
 };
@@ -191,6 +229,10 @@ struct C : virtual simple::A {
   // VTABLE-EXTENDED-C-NEXT:     [this adjustment: vtordisp at -4, vbptr at 12 to the left,
   // VTABLE-EXTENDED-C-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
 
+  // VTABLE-EXTENDED-C: Thunks for 'void simple::A::f()' (1 entry).
+  // VTABLE-EXTENDED-C-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 12 to the left,
+  // VTABLE-EXTENDED-C-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
+
   // `vtordispex{12,8,4294967292,8}'
   // MANGLING-DAG: @"\01?f at A@simple@@$R4M at 7PPPPPPPM@7AEXXZ"
   int x;
@@ -212,6 +254,10 @@ struct E : virtual D {
   // VTABLE-EXTENDED-E-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left,
   // VTABLE-EXTENDED-E-NEXT:      vboffset at 8 in the vbtable, 12 non-virtual]
 
+  // VTABLE-EXTENDED-E: Thunks for 'void extended::D::f()' (1 entry).
+  // VTABLE-EXTENDED-E-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
+  // VTABLE-EXTENDED-E-NEXT:      vboffset at 8 in the vbtable, 12 non-virtual]
+
   // `vtordispex{8,8,4294967292,12}'
   // MANGLING-DAG: @"\01?f at D@extended@@$R477PPPPPPPM at M@AEXXZ"
 
@@ -227,6 +273,10 @@ struct F : virtual Z, virtual D {
   // VTABLE-EXTENDED-F-NEXT:     [this adjustment: vtordisp at -4, vbptr at 20 to the left,
   // VTABLE-EXTENDED-F-NEXT:      vboffset at 12 in the vbtable, 12 non-virtual]
 
+  // VTABLE-EXTENDED-F: Thunks for 'void extended::D::f()' (1 entry).
+  // VTABLE-EXTENDED-F-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 20 to the left,
+  // VTABLE-EXTENDED-F-NEXT:      vboffset at 12 in the vbtable, 12 non-virtual]
+
   // `vtordispex{20,12,4294967292,12}'
   // MANGLING-DAG: @"\01?f at D@extended@@$R4BE at M@PPPPPPPM at M@AEXXZ"
   int x;
@@ -247,6 +297,10 @@ struct G : virtual simple::A {
   // VTABLE-EXTENDED-G-NEXT: 1 | extended::G::~G() [scalar deleting]
   // VTABLE-EXTENDED-G-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
 
+  // VTABLE-EXTENDED-G: Thunks for 'void simple::A::f()' (1 entry).
+  // VTABLE-EXTENDED-G-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
+  // VTABLE-EXTENDED-G-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
+
   // Emits a G's own vfptr, thus moving the vbptr in the layout.
   virtual void g();
 
@@ -267,6 +321,10 @@ struct H : Z, A {
   // VTABLE-EXTENDED-H-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left,
   // VTABLE-EXTENDED-H-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
 
+  // VTABLE-EXTENDED-H: Thunks for 'void simple::A::f()' (1 entry).
+  // VTABLE-EXTENDED-H-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
+  // VTABLE-EXTENDED-H-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
+
   // MANGLING-DAG: @"\01?f at A@simple@@$R477PPPPPPPM at 7AEXXZ"
   // MANGLING-DAG: @"\01??_EH at extended@@$4PPPPPPPM at BA@AEPAXI at Z"
 };
@@ -284,6 +342,10 @@ struct A : virtual simple::B {
   // VTABLE-PR17738-A-NEXT:     [this adjustment: vtordisp at -12, vbptr at 20 to the left,
   // VTABLE-PR17738-A-NEXT:      vboffset at 8 in the vbtable, 16 non-virtual]
 
+  // VTABLE-PR17738-A: Thunks for 'void simple::B::f()' (1 entry).
+  // VTABLE-PR17738-A-NEXT: 0 | [this adjustment: vtordisp at -12, vbptr at 20 to the left,
+  // VTABLE-PR17738-A-NEXT:      vboffset at 8 in the vbtable, 16 non-virtual]
+
   // MANGLING-DAG: @"\01?f at B@simple@@$R4BE at 7PPPPPPPE@BA at AEXXZ"
   int a;
   virtual ~A();

Modified: cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp?rev=203222&r1=203221&r2=203222&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp Fri Mar  7 03:34:59 2014
@@ -11,6 +11,7 @@
 // RUN: FileCheck --check-prefix=TEST7 %s < %t
 // RUN: FileCheck --check-prefix=TEST8-X %s < %t
 // RUN: FileCheck --check-prefix=TEST8-Z %s < %t
+// RUN: FileCheck --check-prefix=TEST8-T %s < %t
 // RUN: FileCheck --check-prefix=TEST9-Y %s < %t
 // RUN: FileCheck --check-prefix=TEST9-Z %s < %t
 // RUN: FileCheck --check-prefix=TEST9-W %s < %t
@@ -48,7 +49,7 @@ struct C: virtual A {
 
   // MANGLING-DAG: @"\01??_7C@@6B@"
 
-  virtual void f();
+  virtual void f() {}
 };
 
 C c;
@@ -156,6 +157,9 @@ struct X: virtual C {
   // TEST4-NEXT:     [this adjustment: 8 non-virtual]
   // TEST4-NEXT: 1 | void A::z()
 
+  // TEST4: Thunks for 'void C::f()' (1 entry).
+  // TEST4-NEXT: 0 | [this adjustment: 8 non-virtual]
+
   // TEST4-NOT: VFTable indices for 'Test4::X'
 
   // MANGLING-DAG: @"\01??_7X at Test4@@6B@"
@@ -260,7 +264,7 @@ X x;
 // Another diamond inheritance which led to AST crashes.
 struct Y : virtual A {};
 
-class Z : Y, C {
+struct Z : Y, C {
   // TEST8-Z: VFTable for 'A' in 'Test8::Y' in 'Test8::Z' (2 entries).
   // TEST8-Z-NEXT: 0 | void Test8::Z::f()
   // TEST8-Z-NEXT: 1 | void A::z()
@@ -271,6 +275,27 @@ class Z : Y, C {
   virtual void f();
 };
 Z z;
+
+// Another diamond inheritance which we miscompiled (PR18967).
+struct W : virtual A {
+  virtual void bar();
+};
+
+struct T : W, C {
+  // TEST8-T: VFTable for 'Test8::W' in 'Test8::T' (1 entry)
+  // TEST8-T-NEXT: 0 | void Test8::T::bar()
+
+  // TEST8-T: VFTable for 'A' in 'Test8::W' in 'Test8::T' (2 entries)
+  // TEST8-T-NEXT: 0 | void C::f()
+  // TEST8-T-NEXT:     [this adjustment: -4 non-virtual]
+  // TEST8-T-NEXT: 1 | void A::z()
+
+  // TEST8-T: Thunks for 'void C::f()' (1 entry).
+  // TEST8-T-NEXT: 0 | [this adjustment: -4 non-virtual]
+  virtual void bar();
+  int field;
+};
+T t;
 }
 
 namespace Test9 {
@@ -473,7 +498,7 @@ struct U : virtual W {
   // VDTORS-U-NEXT:     [this adjustment: -4 non-virtual]
   // VDTORS-U-NEXT: 1 | void vdtors::X::zzz()
 
-  // VDTORS-U: Thunks for 'vdtors::W::~W()' (1 entry).
+  // VDTORS-U: Thunks for 'vdtors::U::~U()' (1 entry).
   // VDTORS-U-NEXT: 0 | [this adjustment: -4 non-virtual]
 
   // VDTORS-U: VFTable indices for 'vdtors::U' (1 entry).
@@ -493,7 +518,7 @@ struct V : virtual W {
   // VDTORS-V-NEXT:     [this adjustment: -4 non-virtual]
   // VDTORS-V-NEXT: 1 | void vdtors::X::zzz()
 
-  // VDTORS-V: Thunks for 'vdtors::W::~W()' (1 entry).
+  // VDTORS-V: Thunks for 'vdtors::V::~V()' (1 entry).
   // VDTORS-V-NEXT: 0 | [this adjustment: -4 non-virtual]
 
   // VDTORS-V: VFTable indices for 'vdtors::V' (1 entry).
@@ -537,9 +562,12 @@ struct Z {
 struct W : Z {
   // RET-W: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' (2 entries).
   // RET-W-NEXT: 0 | return_adjustment::X *return_adjustment::W::foo()
-  // RET-W-NEXT:     [return adjustment: vbase #1, 0 non-virtual]
+  // RET-W-NEXT:     [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
   // RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo()
 
+  // RET-W: Thunks for 'return_adjustment::X *return_adjustment::W::foo()' (1 entry).
+  // RET-W-NEXT: 0 | [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
+
   // RET-W: VFTable indices for 'return_adjustment::W' (1 entry).
   // RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo()
 
@@ -551,11 +579,15 @@ W y;
 struct T : W {
   // RET-T: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' in 'return_adjustment::T' (3 entries).
   // RET-T-NEXT: 0 | return_adjustment::Y *return_adjustment::T::foo()
-  // RET-T-NEXT:     [return adjustment: vbase #1, 0 non-virtual]
+  // RET-T-NEXT:     [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
   // RET-T-NEXT: 1 | return_adjustment::Y *return_adjustment::T::foo()
-  // RET-T-NEXT:     [return adjustment: vbase #2, 0 non-virtual]
+  // RET-T-NEXT:     [return adjustment (to type 'struct return_adjustment::X *'): vbase #2, 0 non-virtual]
   // RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo()
 
+  // RET-T: Thunks for 'return_adjustment::Y *return_adjustment::T::foo()' (2 entries).
+  // RET-T-NEXT: 0 | [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
+  // RET-T-NEXT: 1 | [return adjustment (to type 'struct return_adjustment::X *'): vbase #2, 0 non-virtual]
+
   // RET-T: VFTable indices for 'return_adjustment::T' (1 entry).
   // RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo()
 
@@ -571,9 +603,12 @@ struct U : virtual A {
 struct V : Z {
   // RET-V: VFTable for 'return_adjustment::Z' in 'return_adjustment::V' (2 entries).
   // RET-V-NEXT: 0 | return_adjustment::U *return_adjustment::V::foo()
-  // RET-V-NEXT:     [return adjustment: vbptr at offset 4, vbase #1, 0 non-virtual]
+  // RET-V-NEXT:     [return adjustment (to type 'struct A *'): vbptr at offset 4, vbase #1, 0 non-virtual]
   // RET-V-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo()
 
+  // RET-V: Thunks for 'return_adjustment::U *return_adjustment::V::foo()' (1 entry).
+  // RET-V-NEXT: 0 | [return adjustment (to type 'struct A *'): vbptr at offset 4, vbase #1, 0 non-virtual]
+
   // RET-V: VFTable indices for 'return_adjustment::V' (1 entry).
   // RET-V-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo()
 





More information about the cfe-commits mailing list