https://github.com/tbaederr created 
https://github.com/llvm/llvm-project/pull/179050

Add a path and an `IsDerivedMember` member to our `MemberPointer` class. Fix 
base-to-derived/derived-to-base casts. Add tests and unit tests, since regular 
tests allow a lot and we want to check the path size exactly.

>From 7c75dbb6d1c54b44951eba50fa514314ee149169 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Sat, 31 Jan 2026 07:31:56 +0100
Subject: [PATCH] Memberpointers

---
 clang/lib/AST/ByteCode/Compiler.cpp        |  60 +++++++----
 clang/lib/AST/ByteCode/Interp.cpp          | 112 +++++++++++++++++++--
 clang/lib/AST/ByteCode/Interp.h            |  20 ++--
 clang/lib/AST/ByteCode/InterpState.h       |   5 +
 clang/lib/AST/ByteCode/MemberPointer.cpp   |  15 +--
 clang/lib/AST/ByteCode/MemberPointer.h     |  95 +++++++++++++----
 clang/lib/AST/ByteCode/Opcodes.td          |   8 +-
 clang/lib/AST/ByteCode/PrimType.h          |   6 +-
 clang/test/AST/ByteCode/memberpointers.cpp |  27 +++++
 clang/unittests/AST/ByteCode/toAPValue.cpp |  94 ++++++++++++++++-
 10 files changed, 376 insertions(+), 66 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index da1db48c55a07..7500db2c29c89 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -269,34 +269,61 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) 
{
   }
 
   case CK_DerivedToBaseMemberPointer: {
-    assert(classifyPrim(CE->getType()) == PT_MemberPtr);
-    assert(classifyPrim(SubExpr->getType()) == PT_MemberPtr);
-    const auto *FromMP = SubExpr->getType()->castAs<MemberPointerType>();
-    const auto *ToMP = CE->getType()->castAs<MemberPointerType>();
-
-    unsigned DerivedOffset =
-        Ctx.collectBaseOffset(ToMP->getMostRecentCXXRecordDecl(),
-                              FromMP->getMostRecentCXXRecordDecl());
+    assert(classifyPrim(CE) == PT_MemberPtr);
+    assert(classifyPrim(SubExpr) == PT_MemberPtr);
 
     if (!this->delegate(SubExpr))
       return false;
 
-    return this->emitGetMemberPtrBasePop(DerivedOffset, CE);
+    const CXXRecordDecl *CurDecl = SubExpr->getType()
+                                       ->castAs<MemberPointerType>()
+                                       ->getMostRecentCXXRecordDecl();
+    for (const CXXBaseSpecifier *B : CE->path()) {
+      const CXXRecordDecl *ToDecl = B->getType()->getAsCXXRecordDecl();
+      unsigned DerivedOffset = Ctx.collectBaseOffset(ToDecl, CurDecl);
+
+      if (!this->emitCastMemberPtrBasePop(DerivedOffset, ToDecl, CE))
+        return false;
+      CurDecl = ToDecl;
+    }
+
+    return true;
   }
 
   case CK_BaseToDerivedMemberPointer: {
     assert(classifyPrim(CE) == PT_MemberPtr);
     assert(classifyPrim(SubExpr) == PT_MemberPtr);
-    const auto *FromMP = SubExpr->getType()->castAs<MemberPointerType>();
-    const auto *ToMP = CE->getType()->castAs<MemberPointerType>();
-
-    unsigned DerivedOffset =
-        Ctx.collectBaseOffset(FromMP->getMostRecentCXXRecordDecl(),
-                              ToMP->getMostRecentCXXRecordDecl());
 
     if (!this->delegate(SubExpr))
       return false;
-    return this->emitGetMemberPtrBasePop(-DerivedOffset, CE);
+
+    const CXXRecordDecl *CurDecl = SubExpr->getType()
+                                       ->castAs<MemberPointerType>()
+                                       ->getMostRecentCXXRecordDecl();
+    // Base-to-derived member pointer casts store the path in derived-to-base
+    // order, so iterate backwards. The CXXBaseSpecifier also provides us with
+    // the wrong end of the derived->base arc, so stagger the path by one 
class.
+    typedef std::reverse_iterator<CastExpr::path_const_iterator> ReverseIter;
+    for (ReverseIter PathI(CE->path_end() - 1), PathE(CE->path_begin());
+         PathI != PathE; ++PathI) {
+      const CXXRecordDecl *ToDecl = (*PathI)->getType()->getAsCXXRecordDecl();
+      unsigned DerivedOffset = Ctx.collectBaseOffset(CurDecl, ToDecl);
+
+      if (!this->emitCastMemberPtrDerivedPop(-DerivedOffset, ToDecl, CE))
+        return false;
+      CurDecl = ToDecl;
+    }
+
+    const CXXRecordDecl *ToDecl = CE->getType()
+                                      ->castAs<MemberPointerType>()
+                                      ->getMostRecentCXXRecordDecl();
+    assert(ToDecl != CurDecl);
+    unsigned DerivedOffset = Ctx.collectBaseOffset(CurDecl, ToDecl);
+
+    if (!this->emitCastMemberPtrDerivedPop(-DerivedOffset, ToDecl, CE))
+      return false;
+
+    return true;
   }
 
   case CK_UncheckedDerivedToBase:
@@ -7559,7 +7586,6 @@ bool Compiler<Emitter>::emitDestructionPop(const 
Descriptor *Desc,
 template <class Emitter>
 bool Compiler<Emitter>::emitDummyPtr(const DeclTy &D, const Expr *E) {
   assert(!DiscardResult && "Should've been checked before");
-
   unsigned DummyID = P.getOrCreateDummy(D);
 
   if (!this->emitGetPtrGlobal(DummyID, E))
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 2a495a475c378..699cf3c041464 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -2336,7 +2336,6 @@ bool arePotentiallyOverlappingStringLiterals(const 
Pointer &LHS,
 
 static void copyPrimitiveMemory(InterpState &S, const Pointer &Ptr,
                                 PrimType T) {
-
   if (T == PT_IntAPS) {
     auto &Val = Ptr.deref<IntegralAP<true>>();
     if (!Val.singleWord()) {
@@ -2355,16 +2354,34 @@ static void copyPrimitiveMemory(InterpState &S, const 
Pointer &Ptr,
       uint64_t *NewMemory = new (S.P) uint64_t[Val.numWords()];
       Val.take(NewMemory);
     }
+  } else if (T == PT_MemberPtr) {
+    auto &Val = Ptr.deref<MemberPointer>();
+    unsigned PathLength = Val.getPathLength();
+    auto *NewPath = new (S.P) const CXXRecordDecl *[PathLength];
+    for (unsigned I = 0; I != PathLength; ++I) {
+      NewPath[I] = Val.getPathEntry(I);
+    }
+    Val.takePath(NewPath);
   }
 }
 
 template <typename T>
 static void copyPrimitiveMemory(InterpState &S, const Pointer &Ptr) {
   assert(needsAlloc<T>());
-  auto &Val = Ptr.deref<T>();
-  if (!Val.singleWord()) {
-    uint64_t *NewMemory = new (S.P) uint64_t[Val.numWords()];
-    Val.take(NewMemory);
+  if constexpr (std::is_same_v<T, MemberPointer>) {
+    auto &Val = Ptr.deref<MemberPointer>();
+    unsigned PathLength = Val.getPathLength();
+    auto *NewPath = new (S.P) const CXXRecordDecl *[PathLength];
+    for (unsigned I = 0; I != PathLength; ++I) {
+      NewPath[I] = Val.getPathEntry(I);
+    }
+    Val.takePath(NewPath);
+  } else {
+    auto &Val = Ptr.deref<T>();
+    if (!Val.singleWord()) {
+      uint64_t *NewMemory = new (S.P) uint64_t[Val.numWords()];
+      Val.take(NewMemory);
+    }
   }
 }
 
@@ -2375,9 +2392,9 @@ static void finishGlobalRecurse(InterpState &S, const 
Pointer &Ptr) {
         TYPE_SWITCH_ALLOC(Fi.Desc->getPrimType(), {
           copyPrimitiveMemory<T>(S, Ptr.atField(Fi.Offset));
         });
-        copyPrimitiveMemory(S, Ptr.atField(Fi.Offset), Fi.Desc->getPrimType());
-      } else
+      } else {
         finishGlobalRecurse(S, Ptr.atField(Fi.Offset));
+      }
     }
     return;
   }
@@ -2491,6 +2508,87 @@ bool Destroy(InterpState &S, CodePtr OpPC, uint32_t I) {
   return true;
 }
 
+// Perform a cast towards the class of the Decl (either up or down the
+// hierarchy).
+static bool castBackMemberPointer(InterpState &S,
+                                  const MemberPointer &MemberPtr,
+                                  int32_t BaseOffset,
+                                  const RecordDecl *BaseDecl) {
+  const CXXRecordDecl *Expected;
+  if (MemberPtr.getPathLength() >= 2)
+    Expected = MemberPtr.getPathEntry(MemberPtr.getPathLength() - 2);
+  else
+    Expected = MemberPtr.getRecordDecl();
+
+  assert(Expected);
+  if (Expected->getCanonicalDecl() != BaseDecl->getCanonicalDecl()) {
+    // C++11 [expr.static.cast]p12: In a conversion from (D::*) to (B::*),
+    // if B does not contain the original member and is not a base or
+    // derived class of the class containing the original member, the result
+    // of the cast is undefined.
+    // C++11 [conv.mem]p2 does not cover this case for a cast from (B::*) to
+    // (D::*). We consider that to be a language defect.
+    return false;
+  }
+
+  unsigned OldPathLength = MemberPtr.getPathLength();
+  unsigned NewPathLength = OldPathLength - 1;
+  bool IsDerivedMember = NewPathLength != 0;
+  auto NewPath = S.allocMemberPointerPath(NewPathLength);
+  for (unsigned I = 0; I != NewPathLength; ++I) {
+    NewPath[I] = MemberPtr.getPathEntry(I);
+  }
+
+  S.Stk.push<MemberPointer>(MemberPtr.atInstanceBase(BaseOffset, NewPathLength,
+                                                     NewPath, 
IsDerivedMember));
+  return true;
+}
+
+static bool appendToMemberPointer(InterpState &S,
+                                  const MemberPointer &MemberPtr,
+                                  int32_t BaseOffset,
+                                  const RecordDecl *BaseDecl,
+                                  bool IsDerivedMember) {
+  unsigned OldPathLength = MemberPtr.getPathLength();
+  unsigned NewPathLength = OldPathLength + 1;
+
+  auto NewPath = S.allocMemberPointerPath(NewPathLength);
+  for (unsigned I = 0; I != OldPathLength; ++I) {
+    NewPath[I] = MemberPtr.getPathEntry(I);
+  }
+  NewPath[OldPathLength] = cast<CXXRecordDecl>(BaseDecl);
+
+  S.Stk.push<MemberPointer>(MemberPtr.atInstanceBase(BaseOffset, NewPathLength,
+                                                     NewPath, 
IsDerivedMember));
+  return true;
+}
+
+/// DerivedToBaseMemberPointer
+bool CastMemberPtrBasePop(InterpState &S, CodePtr OpPC, int32_t Off,
+                          const RecordDecl *BaseDecl) {
+  const auto &Ptr = S.Stk.pop<MemberPointer>();
+
+  if (!Ptr.isDerivedMember() && Ptr.hasPath())
+    return castBackMemberPointer(S, Ptr, Off, BaseDecl);
+
+  bool IsDerivedMember = Ptr.isDerivedMember() || !Ptr.hasPath();
+  return appendToMemberPointer(S, Ptr, Off, BaseDecl, IsDerivedMember);
+}
+
+/// BaseToDerivedMemberPointer
+bool CastMemberPtrDerivedPop(InterpState &S, CodePtr OpPC, int32_t Off,
+                             const RecordDecl *BaseDecl) {
+  const auto &Ptr = S.Stk.pop<MemberPointer>();
+
+  if (!Ptr.isDerivedMember()) {
+    // Simply append.
+    return appendToMemberPointer(S, Ptr, Off, BaseDecl,
+                                 /*IsDerivedMember=*/false);
+  }
+
+  return castBackMemberPointer(S, Ptr, Off, BaseDecl);
+}
+
 // https://github.com/llvm/llvm-project/issues/102513
 #if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG)
 #pragma optimize("", off)
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index d856cd7c0a2d9..6a04bdbf73bb1 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -217,6 +217,12 @@ bool CheckDeclRef(InterpState &S, CodePtr OpPC, const 
DeclRefExpr *DR);
 bool InvalidDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR,
                     bool InitializerFailed);
 
+/// DerivedToBaseMemberPointer
+bool CastMemberPtrBasePop(InterpState &S, CodePtr OpPC, int32_t Off,
+                          const RecordDecl *BaseDecl);
+/// BaseToDerivedMemberPointer
+bool CastMemberPtrDerivedPop(InterpState &S, CodePtr OpPC, int32_t Off,
+                             const RecordDecl *BaseDecl);
 enum class ArithOp { Add, Sub };
 
 
//===----------------------------------------------------------------------===//
@@ -1544,6 +1550,14 @@ bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t 
I) {
       Val.take(NewMemory);
     }
 
+  } else if constexpr (std::is_same_v<T, MemberPointer>) {
+    auto &Val = P.deref<MemberPointer>();
+    unsigned PathLength = Val.getPathLength();
+    auto *NewPath = new (S.P) const CXXRecordDecl *[PathLength];
+    for (unsigned I = 0; I != PathLength; ++I) {
+      NewPath[I] = Val.getPathEntry(I);
+    }
+    Val.takePath(NewPath);
   } else if constexpr (needsAlloc<T>()) {
     auto &Val = P.deref<T>();
     if (!Val.singleWord()) {
@@ -1869,12 +1883,6 @@ inline bool GetPtrBasePop(InterpState &S, CodePtr OpPC, 
uint32_t Off,
   return true;
 }
 
-inline bool GetMemberPtrBasePop(InterpState &S, CodePtr OpPC, int32_t Off) {
-  const auto &Ptr = S.Stk.pop<MemberPointer>();
-  S.Stk.push<MemberPointer>(Ptr.atInstanceBase(Off));
-  return true;
-}
-
 inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
   if (S.checkingPotentialConstantExpression())
     return false;
diff --git a/clang/lib/AST/ByteCode/InterpState.h 
b/clang/lib/AST/ByteCode/InterpState.h
index 231b51124c70d..4ba1a2666307b 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -146,6 +146,11 @@ class InterpState final : public State, public 
SourceMapper {
     return Floating(Mem, llvm::APFloatBase::SemanticsToEnum(Sem));
   }
 
+  const CXXRecordDecl **allocMemberPointerPath(unsigned Length) {
+    return reinterpret_cast<const CXXRecordDecl **>(
+        this->allocate(Length * sizeof(CXXRecordDecl *)));
+  }
+
 private:
   friend class EvaluationResult;
   friend class InterpStateCCOverride;
diff --git a/clang/lib/AST/ByteCode/MemberPointer.cpp 
b/clang/lib/AST/ByteCode/MemberPointer.cpp
index 8b1b0187818e9..ddd654238af17 100644
--- a/clang/lib/AST/ByteCode/MemberPointer.cpp
+++ b/clang/lib/AST/ByteCode/MemberPointer.cpp
@@ -16,9 +16,9 @@ namespace clang {
 namespace interp {
 
 std::optional<Pointer> MemberPointer::toPointer(const Context &Ctx) const {
-  if (!Dcl || isa<FunctionDecl>(Dcl))
+  if (!getDecl() || isa<FunctionDecl>(getDecl()))
     return Base;
-  assert((isa<FieldDecl, IndirectFieldDecl>(Dcl)));
+  assert((isa<FieldDecl, IndirectFieldDecl>(getDecl())));
 
   if (!Base.isBlockPointer())
     return std::nullopt;
@@ -42,7 +42,7 @@ std::optional<Pointer> MemberPointer::toPointer(const Context 
&Ctx) const {
   unsigned Offset = 0;
   Offset += BlockMDSize;
 
-  if (const auto *FD = dyn_cast<FieldDecl>(Dcl)) {
+  if (const auto *FD = dyn_cast<FieldDecl>(getDecl())) {
     if (FD->getParent() == BaseRecord->getDecl())
       return CastedBase.atField(BaseRecord->getField(FD)->Offset);
 
@@ -58,7 +58,7 @@ std::optional<Pointer> MemberPointer::toPointer(const Context 
&Ctx) const {
       Offset += Ctx.collectBaseOffset(FieldParent, BaseDecl);
 
   } else {
-    const auto *IFD = cast<IndirectFieldDecl>(Dcl);
+    const auto *IFD = cast<IndirectFieldDecl>(getDecl());
 
     for (const NamedDecl *ND : IFD->chain()) {
       const FieldDecl *F = cast<FieldDecl>(ND);
@@ -77,7 +77,8 @@ std::optional<Pointer> MemberPointer::toPointer(const Context 
&Ctx) const {
 }
 
 FunctionPointer MemberPointer::toFunctionPointer(const Context &Ctx) const {
-  return 
FunctionPointer(Ctx.getProgram().getFunction(cast<FunctionDecl>(Dcl)));
+  return FunctionPointer(
+      Ctx.getProgram().getFunction(cast<FunctionDecl>(getDecl())));
 }
 
 APValue MemberPointer::toAPValue(const ASTContext &ASTCtx) const {
@@ -88,8 +89,8 @@ APValue MemberPointer::toAPValue(const ASTContext &ASTCtx) 
const {
   if (hasBase())
     return Base.toAPValue(ASTCtx);
 
-  return APValue(getDecl(), /*IsDerivedMember=*/false,
-                 /*Path=*/{});
+  return APValue(getDecl(), /*IsDerivedMember=*/isDerivedMember(),
+                 /*Path=*/ArrayRef(Path, PathLength));
 }
 
 } // namespace interp
diff --git a/clang/lib/AST/ByteCode/MemberPointer.h 
b/clang/lib/AST/ByteCode/MemberPointer.h
index 8dd75cad092c0..8062cdd7c1f78 100644
--- a/clang/lib/AST/ByteCode/MemberPointer.h
+++ b/clang/lib/AST/ByteCode/MemberPointer.h
@@ -10,10 +10,12 @@
 #define LLVM_CLANG_AST_INTERP_MEMBER_POINTER_H
 
 #include "Pointer.h"
+#include "llvm/ADT/PointerIntPair.h"
 #include <optional>
 
 namespace clang {
 class ASTContext;
+class CXXRecordDecl;
 namespace interp {
 
 class Context;
@@ -22,21 +24,33 @@ class FunctionPointer;
 class MemberPointer final {
 private:
   Pointer Base;
-  const ValueDecl *Dcl = nullptr;
+  /// The member declaration, and a flag indicating
+  /// whether the member is a member of some class derived from the class type
+  /// of the member pointer.
+  llvm::PointerIntPair<const ValueDecl *, 1, bool> DeclAndIsDerivedMember;
+  /// The path of base/derived classes from the member declaration's
+  /// class (exclusive) to the class type of the member pointer (inclusive).
+  /// This a allocated by the InterpState or the Program.
+  const CXXRecordDecl **Path = nullptr;
   int32_t PtrOffset = 0;
+  uint8_t PathLength = 0;
 
-  MemberPointer(Pointer Base, const ValueDecl *Dcl, int32_t PtrOffset)
-      : Base(Base), Dcl(Dcl), PtrOffset(PtrOffset) {}
+  MemberPointer(Pointer Base, const ValueDecl *Dcl, int32_t PtrOffset,
+                uint8_t PathLength = 0, const CXXRecordDecl **Path = nullptr,
+                bool IsDerived = false)
+      : Base(Base), DeclAndIsDerivedMember(Dcl, IsDerived), Path(Path),
+        PtrOffset(PtrOffset), PathLength(PathLength) {}
 
 public:
   MemberPointer() = default;
-  MemberPointer(Pointer Base, const ValueDecl *Dcl) : Base(Base), Dcl(Dcl) {}
+  MemberPointer(Pointer Base, const ValueDecl *Dcl)
+      : Base(Base), DeclAndIsDerivedMember(Dcl) {}
   MemberPointer(uint32_t Address, const Descriptor *D) {
     // We only reach this for Address == 0, when creating a null member 
pointer.
     assert(Address == 0);
   }
 
-  MemberPointer(const ValueDecl *D) : Dcl(D) {
+  MemberPointer(const ValueDecl *D) : DeclAndIsDerivedMember(D) {
     assert((isa<FieldDecl, IndirectFieldDecl, CXXMethodDecl>(D)));
   }
 
@@ -47,6 +61,24 @@ class MemberPointer final {
     return 17;
   }
 
+  bool hasDecl() const { return DeclAndIsDerivedMember.getPointer(); }
+  bool isDerivedMember() const { return DeclAndIsDerivedMember.getInt(); }
+  const ValueDecl *getDecl() const {
+    return DeclAndIsDerivedMember.getPointer();
+  }
+  bool hasPath() const { return PathLength != 0; }
+  unsigned getPathLength() const { return PathLength; }
+  const CXXRecordDecl *getPathEntry(unsigned Index) const {
+    return Path[Index];
+  }
+  void takePath(const CXXRecordDecl **NewPath) {
+    assert(Path != NewPath);
+    Path = NewPath;
+  }
+
+  // Pretend we always have a path.
+  bool singleWord() const { return false; }
+
   std::optional<Pointer> toPointer(const Context &Ctx) const;
 
   FunctionPointer toFunctionPointer(const Context &Ctx) const;
@@ -63,32 +95,44 @@ class MemberPointer final {
     return Base.atFieldSub(PtrOffset);
   }
   bool isMemberFunctionPointer() const {
-    return isa_and_nonnull<CXXMethodDecl>(Dcl);
+    return isa_and_nonnull<CXXMethodDecl>(DeclAndIsDerivedMember.getPointer());
   }
   const CXXMethodDecl *getMemberFunction() const {
-    return dyn_cast_if_present<CXXMethodDecl>(Dcl);
+    return dyn_cast_if_present<CXXMethodDecl>(
+        DeclAndIsDerivedMember.getPointer());
   }
   const FieldDecl *getField() const {
-    return dyn_cast_if_present<FieldDecl>(Dcl);
+    return dyn_cast_if_present<FieldDecl>(DeclAndIsDerivedMember.getPointer());
   }
 
-  bool hasDecl() const { return Dcl; }
-  const ValueDecl *getDecl() const { return Dcl; }
+  const CXXRecordDecl *getRecordDecl() const {
+    if (const FieldDecl *FD = getField())
+      return cast<CXXRecordDecl>(FD->getParent());
 
-  MemberPointer atInstanceBase(unsigned Offset) const {
+    if (const CXXMethodDecl *MD = getMemberFunction())
+      return MD->getParent();
+    return nullptr;
+  }
+
+  MemberPointer atInstanceBase(unsigned Offset, uint8_t PathLength = 0,
+                               const CXXRecordDecl **Path = nullptr,
+                               bool NewIsDerived = false) const {
     if (Base.isZero())
-      return MemberPointer(Base, Dcl, Offset);
-    return MemberPointer(this->Base, Dcl, Offset + PtrOffset);
+      return MemberPointer(Base, DeclAndIsDerivedMember.getPointer(), Offset,
+                           PathLength, Path, NewIsDerived);
+    return MemberPointer(this->Base, DeclAndIsDerivedMember.getPointer(),
+                         Offset + PtrOffset, PathLength, Path, NewIsDerived);
   }
 
   MemberPointer takeInstance(Pointer Instance) const {
     assert(this->Base.isZero());
-    return MemberPointer(Instance, this->Dcl, this->PtrOffset);
+    return MemberPointer(Instance, DeclAndIsDerivedMember.getPointer(),
+                         this->PtrOffset);
   }
 
   APValue toAPValue(const ASTContext &) const;
 
-  bool isZero() const { return Base.isZero() && !Dcl; }
+  bool isZero() const { return Base.isZero() && !hasDecl(); }
   bool hasBase() const { return !Base.isZero(); }
   bool isWeak() const {
     if (const auto *MF = getMemberFunction())
@@ -97,22 +141,33 @@ class MemberPointer final {
   }
 
   void print(llvm::raw_ostream &OS) const {
-    OS << "MemberPtr(" << Base << " " << (const void *)Dcl << " + " << 
PtrOffset
-       << ")";
+    OS << "MemberPtr(" << Base << " " << (const void *)getDecl() << " + "
+       << PtrOffset << ")";
   }
 
   std::string toDiagnosticString(const ASTContext &Ctx) const {
-    return toAPValue(Ctx).getAsString(Ctx, Dcl->getType());
+    return toAPValue(Ctx).getAsString(Ctx, getDecl()->getType());
   }
 
   ComparisonCategoryResult compare(const MemberPointer &RHS) const {
-    if (this->Dcl == RHS.Dcl)
+    if (this->getDecl() == RHS.getDecl()) {
+
+      if (this->PathLength != RHS.PathLength)
+        return ComparisonCategoryResult::Unordered;
+
+      if (PathLength != 0 &&
+          std::memcmp(Path, RHS.Path, PathLength * sizeof(CXXRecordDecl *)) !=
+              0)
+        return ComparisonCategoryResult::Unordered;
+
       return ComparisonCategoryResult::Equal;
+    }
     return ComparisonCategoryResult::Unordered;
   }
 };
 
-inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, MemberPointer FP) {
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+                                     const MemberPointer &FP) {
   FP.print(OS);
   return OS;
 }
diff --git a/clang/lib/AST/ByteCode/Opcodes.td 
b/clang/lib/AST/ByteCode/Opcodes.td
index e701c954e00c2..2281b3f8dd438 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -332,9 +332,13 @@ def GetPtrThisField : OffsetOpcode;
 def GetPtrBase : OffsetOpcode;
 // [Pointer] -> [Pointer]
 def GetPtrBasePop : OffsetOpcode { let Args = [ArgUint32, ArgBool]; }
-def GetMemberPtrBasePop : Opcode {
+def CastMemberPtrBasePop : Opcode {
   // Offset of field, which is a base.
-  let Args = [ArgSint32];
+  let Args = [ArgSint32, ArgRecordDecl];
+}
+def CastMemberPtrDerivedPop : Opcode {
+  // Offset of field, which is a base.
+  let Args = [ArgSint32, ArgRecordDecl];
 }
 
 def FinishInitPop : Opcode;
diff --git a/clang/lib/AST/ByteCode/PrimType.h 
b/clang/lib/AST/ByteCode/PrimType.h
index f0454b484ff98..2433eb33c47b1 100644
--- a/clang/lib/AST/ByteCode/PrimType.h
+++ b/clang/lib/AST/ByteCode/PrimType.h
@@ -128,10 +128,11 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream 
&OS,
 constexpr bool isIntegralType(PrimType T) { return T <= PT_FixedPoint; }
 template <typename T> constexpr bool needsAlloc() {
   return std::is_same_v<T, IntegralAP<false>> ||
-         std::is_same_v<T, IntegralAP<true>> || std::is_same_v<T, Floating>;
+         std::is_same_v<T, IntegralAP<true>> || std::is_same_v<T, Floating> ||
+         std::is_same_v<T, MemberPointer>;
 }
 constexpr bool needsAlloc(PrimType T) {
-  return T == PT_IntAP || T == PT_IntAPS || T == PT_Float;
+  return T == PT_IntAP || T == PT_IntAPS || T == PT_Float || T == PT_MemberPtr;
 }
 
 /// Mapping from primitive types to their representation.
@@ -272,6 +273,7 @@ static inline bool aligned(const void *P) {
       TYPE_SWITCH_CASE(PT_Float, B)                                            
\
       TYPE_SWITCH_CASE(PT_IntAP, B)                                            
\
       TYPE_SWITCH_CASE(PT_IntAPS, B)                                           
\
+      TYPE_SWITCH_CASE(PT_MemberPtr, B)                                        
\
     default:;                                                                  
\
     }                                                                          
\
   } while (0)
diff --git a/clang/test/AST/ByteCode/memberpointers.cpp 
b/clang/test/AST/ByteCode/memberpointers.cpp
index 5d622187e97f2..73019fc65d362 100644
--- a/clang/test/AST/ByteCode/memberpointers.cpp
+++ b/clang/test/AST/ByteCode/memberpointers.cpp
@@ -267,3 +267,30 @@ namespace CastMemberPtrPtrFailed{
   static_assert(S().g(), ""); // both-error {{constant expression}} \
                               // both-note {{in call to 'S().g()'}}
 }
+
+namespace Equality {
+  struct B     { int x; };
+  struct C : B { int z; };
+  static_assert(&C::x == &B::x, "");
+  static_assert(&C::x == &C::x, "");
+
+  constexpr auto A = (int C::*)&B::x;
+  constexpr auto B = (int C::*)&B::x;
+  static_assert(A == B, "");
+
+  struct K {
+    int C::*const M = (int C::*)&B::x;
+  };
+  constexpr K k;
+  static_assert(A== k.M, "");
+
+  constexpr int C::*const MPA[] = {&B::x, &C::x};
+  static_assert(MPA[1] == A, "");
+
+  template<int n> struct T : T<n-1> { const int X = n;};
+  template<> struct T<0> { int n; char k;};
+  template<> struct T<30> : T<29> { int m; };
+
+  constexpr int (T<17>::*deepm) = (int(T<10>::*))&T<30>::m;
+  static_assert(deepm == &T<50>::m, "");
+}
diff --git a/clang/unittests/AST/ByteCode/toAPValue.cpp 
b/clang/unittests/AST/ByteCode/toAPValue.cpp
index 939d08601bb7d..3571dcc41ad27 100644
--- a/clang/unittests/AST/ByteCode/toAPValue.cpp
+++ b/clang/unittests/AST/ByteCode/toAPValue.cpp
@@ -209,11 +209,25 @@ TEST(ToAPValue, FunctionPointersC) {
 }
 
 TEST(ToAPValue, MemberPointers) {
-  constexpr char Code[] = "struct S {\n"
-                          "  int m, n;\n"
-                          "};\n"
-                          "constexpr int S::*pm = &S::m;\n"
-                          "constexpr int S::*nn = nullptr;\n";
+  constexpr char Code[] =
+      "struct S {\n"
+      "  int m, n;\n"
+      "};\n"
+      "constexpr int S::*pm = &S::m;\n"
+      "constexpr int S::*nn = nullptr;\n"
+
+      "struct B{int x;};\n"
+      "struct C : B {int z; };\n"
+      "constexpr auto c1 = (int C::*)&B::x;\n"
+      "constexpr auto D = (int B::*)c1;\n"
+
+      "template<int n> struct T : T<n-1> { const int X = n;};\n"
+      "template<> struct T<0> { int nn_; char kk;};\n"
+      "template<> struct T<30> : T<29> { int mm; };\n"
+      "constexpr auto t1 = (int(T<10>::*))&T<30>::mm;\n"
+      "constexpr auto t2 = (int(T<11>::*))t1;\n"
+      "constexpr auto t3 = (int(T<20>::*))&T<30>::mm;\n"
+      "constexpr int (T<10>::*t4) = &T<0>::nn_;\n";
 
   auto AST = tooling::buildASTFromCodeWithArgs(
       Code, {"-fexperimental-new-constant-interpreter"});
@@ -243,6 +257,8 @@ TEST(ToAPValue, MemberPointers) {
     APValue A = FP.toAPValue(ASTCtx);
     ASSERT_EQ(A.getMemberPointerDecl(), getDecl("m"));
     ASSERT_EQ(A.getKind(), APValue::MemberPointer);
+    ASSERT_EQ(A.getMemberPointerPath().size(), 0u);
+    ASSERT_FALSE(A.isMemberPointerToDerivedMember());
   }
 
   {
@@ -252,6 +268,74 @@ TEST(ToAPValue, MemberPointers) {
     ASSERT_TRUE(NP.isZero());
     APValue A = NP.toAPValue(ASTCtx);
     ASSERT_EQ(A.getKind(), APValue::MemberPointer);
+    ASSERT_EQ(A.getMemberPointerPath().size(), 0u);
+    ASSERT_FALSE(A.isMemberPointerToDerivedMember());
+  }
+
+  {
+    const Pointer &GP = getGlobalPtr("c1");
+    ASSERT_TRUE(GP.isLive());
+    const MemberPointer &MP = GP.deref<MemberPointer>();
+    ASSERT_FALSE(MP.isZero());
+    APValue A = MP.toAPValue(ASTCtx);
+    ASSERT_EQ(A.getKind(), APValue::MemberPointer);
+    ASSERT_EQ(A.getMemberPointerPath().size(), 1u);
+    ASSERT_FALSE(A.isMemberPointerToDerivedMember());
+  }
+
+  {
+    const Pointer &GP = getGlobalPtr("D");
+    ASSERT_TRUE(GP.isLive());
+    const MemberPointer &MP = GP.deref<MemberPointer>();
+    ASSERT_FALSE(MP.isZero());
+    APValue A = MP.toAPValue(ASTCtx);
+    ASSERT_EQ(A.getKind(), APValue::MemberPointer);
+    ASSERT_EQ(A.getMemberPointerPath().size(), 0u);
+    ASSERT_FALSE(A.isMemberPointerToDerivedMember());
+  }
+
+  {
+    const Pointer &GP = getGlobalPtr("t1");
+    ASSERT_TRUE(GP.isLive());
+    const MemberPointer &MP = GP.deref<MemberPointer>();
+    ASSERT_FALSE(MP.isZero());
+    APValue A = MP.toAPValue(ASTCtx);
+    ASSERT_EQ(A.getKind(), APValue::MemberPointer);
+    ASSERT_EQ(A.getMemberPointerPath().size(), 20u);
+    ASSERT_TRUE(A.isMemberPointerToDerivedMember());
+  }
+
+  {
+    const Pointer &GP = getGlobalPtr("t2");
+    ASSERT_TRUE(GP.isLive());
+    const MemberPointer &MP = GP.deref<MemberPointer>();
+    ASSERT_FALSE(MP.isZero());
+    APValue A = MP.toAPValue(ASTCtx);
+    ASSERT_EQ(A.getKind(), APValue::MemberPointer);
+    ASSERT_EQ(A.getMemberPointerPath().size(), 19u);
+    ASSERT_TRUE(A.isMemberPointerToDerivedMember());
+  }
+
+  {
+    const Pointer &GP = getGlobalPtr("t3");
+    ASSERT_TRUE(GP.isLive());
+    const MemberPointer &MP = GP.deref<MemberPointer>();
+    ASSERT_FALSE(MP.isZero());
+    APValue A = MP.toAPValue(ASTCtx);
+    ASSERT_EQ(A.getKind(), APValue::MemberPointer);
+    ASSERT_EQ(A.getMemberPointerPath().size(), 10u);
+    ASSERT_TRUE(A.isMemberPointerToDerivedMember());
+  }
+
+  {
+    const Pointer &GP = getGlobalPtr("t4");
+    ASSERT_TRUE(GP.isLive());
+    const MemberPointer &MP = GP.deref<MemberPointer>();
+    ASSERT_FALSE(MP.isZero());
+    APValue A = MP.toAPValue(ASTCtx);
+    ASSERT_EQ(A.getKind(), APValue::MemberPointer);
+    ASSERT_EQ(A.getMemberPointerPath().size(), 10u);
+    ASSERT_FALSE(A.isMemberPointerToDerivedMember());
   }
 }
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to