[clang] [clang][Interp] Member Pointers (PR #91303)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Sat Jun 1 11:16:41 PDT 2024


Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/91303 at github.com>


https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/91303

>From 30d86295dda9b7aaa06c23b67c54806475266e5d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Wed, 10 Apr 2024 16:42:36 +0200
Subject: [PATCH 1/7] Memberpointers

---
 clang/lib/AST/CMakeLists.txt                  |   1 +
 clang/lib/AST/Interp/ByteCodeExprGen.cpp      | 103 +++++++++-
 clang/lib/AST/Interp/Context.cpp              |  15 +-
 clang/lib/AST/Interp/Context.h                |   2 +
 clang/lib/AST/Interp/Descriptor.cpp           |   1 +
 clang/lib/AST/Interp/Disasm.cpp               |   3 +
 clang/lib/AST/Interp/Interp.cpp               |  30 ++-
 clang/lib/AST/Interp/Interp.h                 |  99 ++++++++++
 clang/lib/AST/Interp/InterpFrame.cpp          |   1 +
 clang/lib/AST/Interp/InterpStack.cpp          |   1 +
 clang/lib/AST/Interp/InterpStack.h            |   3 +
 clang/lib/AST/Interp/MemberPointer.cpp        |  73 +++++++
 clang/lib/AST/Interp/MemberPointer.h          | 112 +++++++++++
 clang/lib/AST/Interp/Opcodes.td               |  18 +-
 clang/lib/AST/Interp/Pointer.cpp              |   1 +
 clang/lib/AST/Interp/Pointer.h                |   1 +
 clang/lib/AST/Interp/PrimType.cpp             |   1 +
 clang/lib/AST/Interp/PrimType.h               |   8 +-
 clang/test/AST/Interp/eval-order.cpp          |   4 +-
 clang/test/AST/Interp/literals.cpp            |   7 +-
 clang/test/AST/Interp/memberpointers.cpp      | 184 ++++++++++++++++++
 .../mangle-ms-templates-memptrs.cpp           |   2 +-
 .../CodeGenCXX/pointers-to-data-members.cpp   |   2 +-
 clang/test/SemaCXX/attr-weak.cpp              |   1 +
 .../SemaCXX/nullptr_in_arithmetic_ops.cpp     |   2 +-
 clang/unittests/AST/Interp/toAPValue.cpp      |  46 +++++
 26 files changed, 692 insertions(+), 29 deletions(-)
 create mode 100644 clang/lib/AST/Interp/MemberPointer.cpp
 create mode 100644 clang/lib/AST/Interp/MemberPointer.h
 create mode 100644 clang/test/AST/Interp/memberpointers.cpp

diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt
index 3faefb54f599f..a5d3dacfc1a84 100644
--- a/clang/lib/AST/CMakeLists.txt
+++ b/clang/lib/AST/CMakeLists.txt
@@ -87,6 +87,7 @@ add_clang_library(clangAST
   Interp/Record.cpp
   Interp/Source.cpp
   Interp/State.cpp
+  Interp/MemberPointer.cpp
   Interp/InterpShared.cpp
   ItaniumCXXABI.cpp
   ItaniumMangle.cpp
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 6607727b5246f..5f8b94c3a0f94 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -100,6 +100,35 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
     return this->emitMemcpy(CE);
   }
 
+  case CK_DerivedToBaseMemberPointer: {
+    assert(classifyPrim(CE->getType()) == PT_MemberPtr);
+    assert(classifyPrim(SubExpr->getType()) == PT_MemberPtr);
+    const auto *FromMP = SubExpr->getType()->getAs<MemberPointerType>();
+    const auto *ToMP = CE->getType()->getAs<MemberPointerType>();
+
+    unsigned DerivedOffset = collectBaseOffset(QualType(ToMP->getClass(), 0),
+                                               QualType(FromMP->getClass(), 0));
+
+    if (!this->visit(SubExpr))
+      return false;
+
+    return this->emitGetMemberPtrBasePop(DerivedOffset, CE);
+  }
+
+  case CK_BaseToDerivedMemberPointer: {
+    assert(classifyPrim(CE) == PT_MemberPtr);
+    assert(classifyPrim(SubExpr) == PT_MemberPtr);
+    const auto *FromMP = SubExpr->getType()->getAs<MemberPointerType>();
+    const auto *ToMP = CE->getType()->getAs<MemberPointerType>();
+
+    unsigned DerivedOffset = collectBaseOffset(QualType(FromMP->getClass(), 0),
+                                               QualType(ToMP->getClass(), 0));
+
+    if (!this->visit(SubExpr))
+      return false;
+    return this->emitGetMemberPtrBasePop(-DerivedOffset, CE);
+  }
+
   case CK_UncheckedDerivedToBase:
   case CK_DerivedToBase: {
     if (!this->visit(SubExpr))
@@ -187,7 +216,8 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
     return this->emitCastFloatingIntegral(*ToT, CE);
   }
 
-  case CK_NullToPointer: {
+  case CK_NullToPointer:
+  case CK_NullToMemberPointer: {
     if (DiscardResult)
       return true;
 
@@ -326,7 +356,8 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
     return this->emitCast(*FromT, *ToT, CE);
   }
 
-  case CK_PointerToBoolean: {
+  case CK_PointerToBoolean:
+  case CK_MemberPointerToBoolean: {
     PrimType PtrT = classifyPrim(SubExpr->getType());
 
     // Just emit p != nullptr for this.
@@ -534,8 +565,23 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
       BO->isComparisonOp())
     return this->emitComplexComparison(LHS, RHS, BO);
 
-  if (BO->isPtrMemOp())
-    return this->visit(RHS);
+  if (BO->isPtrMemOp()) {
+    if (!this->visit(LHS))
+      return false;
+
+    if (!this->visit(RHS))
+      return false;
+
+    if (!this->emitToMemberPtr(BO))
+      return false;
+
+    if (classifyPrim(BO) == PT_MemberPtr)
+      return true;
+
+    if (!this->emitCastMemberPtrPtr(BO))
+      return false;
+    return DiscardResult ? this->emitPopPtr(BO) : true;
+  }
 
   // Typecheck the args.
   std::optional<PrimType> LT = classify(LHS->getType());
@@ -2756,6 +2802,8 @@ bool ByteCodeExprGen<Emitter>::visitZeroInitializer(PrimType T, QualType QT,
     return this->emitNullPtr(nullptr, E);
   case PT_FnPtr:
     return this->emitNullFnPtr(nullptr, E);
+  case PT_MemberPtr:
+    return this->emitNullMemberPtr(nullptr, E);
   case PT_Float: {
     return this->emitConstFloat(APFloat::getZero(Ctx.getFloatSemantics(QT)), E);
   }
@@ -2858,6 +2906,7 @@ bool ByteCodeExprGen<Emitter>::emitConst(T Value, PrimType Ty, const Expr *E) {
     return this->emitConstBool(Value, E);
   case PT_Ptr:
   case PT_FnPtr:
+  case PT_MemberPtr:
   case PT_Float:
   case PT_IntAP:
   case PT_IntAPS:
@@ -3281,10 +3330,28 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
     }
   }
 
+  std::optional<unsigned> CalleeOffset;
   // Add the (optional, implicit) This pointer.
   if (const auto *MC = dyn_cast<CXXMemberCallExpr>(E)) {
-    if (!this->visit(MC->getImplicitObjectArgument()))
-      return false;
+    if (!FuncDecl && classifyPrim(E->getCallee()) == PT_MemberPtr) {
+      // If we end up creating a CallPtr op for this, we need the base of the
+      // member pointer as the instance pointer, and later extract the function
+      // decl as the function pointer.
+      const Expr *Callee = E->getCallee();
+      CalleeOffset =
+          this->allocateLocalPrimitive(Callee, PT_MemberPtr, true, false);
+      if (!this->visit(Callee))
+        return false;
+      if (!this->emitSetLocal(PT_MemberPtr, *CalleeOffset, E))
+        return false;
+      if (!this->emitGetLocal(PT_MemberPtr, *CalleeOffset, E))
+        return false;
+      if (!this->emitGetMemberPtrBase(E))
+        return false;
+    } else {
+      if (!this->visit(MC->getImplicitObjectArgument()))
+        return false;
+    }
   }
 
   llvm::BitVector NonNullArgs = collectNonNullArgs(FuncDecl, Args);
@@ -3352,11 +3419,22 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
     for (unsigned I = 0, N = E->getNumArgs(); I != N; ++I)
       ArgSize += align(primSize(classify(E->getArg(I)).value_or(PT_Ptr)));
 
-    if (!this->visit(E->getCallee()))
-      return false;
+    // Get the callee, either from a member pointer saved in CalleeOffset,
+    // or by just visiting the Callee expr.
+    if (CalleeOffset) {
+      if (!this->emitGetLocal(PT_MemberPtr, *CalleeOffset, E))
+        return false;
+      if (!this->emitGetMemberPtrDecl(E))
+        return false;
+      if (!this->emitCallPtr(ArgSize, E, E))
+        return false;
+    } else {
+      if (!this->visit(E->getCallee()))
+        return false;
 
-    if (!this->emitCallPtr(ArgSize, E, E))
-      return false;
+      if (!this->emitCallPtr(ArgSize, E, E))
+        return false;
+    }
   }
 
   // Cleanup for discarded return values.
@@ -3595,6 +3673,11 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
       return false;
     return DiscardResult ? this->emitPop(*T, E) : true;
   case UO_AddrOf: // &x
+    if (E->getType()->isMemberPointerType()) {
+      // C++11 [expr.unary.op]p3 has very strict rules on how the address of a
+      // member can be formed.
+      return this->emitGetMemberPtr(cast<DeclRefExpr>(SubExpr)->getDecl(), E);
+    }
     // We should already have a pointer when we get here.
     return this->delegate(SubExpr);
   case UO_Deref:  // *x
diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp
index d51a57e5e92ea..a4561cb1c9595 100644
--- a/clang/lib/AST/Interp/Context.cpp
+++ b/clang/lib/AST/Interp/Context.cpp
@@ -160,8 +160,12 @@ std::optional<PrimType> Context::classify(QualType T) const {
   if (T->isFloatingType())
     return PT_Float;
 
+  if (T->isSpecificBuiltinType(BuiltinType::BoundMember) ||
+      T->isMemberPointerType())
+    return PT_MemberPtr;
+
   if (T->isFunctionPointerType() || T->isFunctionReferenceType() ||
-      T->isFunctionType() || T->isSpecificBuiltinType(BuiltinType::BoundMember))
+      T->isFunctionType())
     return PT_FnPtr;
 
   if (T->isReferenceType() || T->isPointerType())
@@ -173,9 +177,6 @@ std::optional<PrimType> Context::classify(QualType T) const {
   if (const auto *DT = dyn_cast<DecltypeType>(T))
     return classify(DT->getUnderlyingType());
 
-  if (const auto *DT = dyn_cast<MemberPointerType>(T))
-    return classify(DT->getPointeeType());
-
   return std::nullopt;
 }
 
@@ -288,10 +289,12 @@ unsigned Context::collectBaseOffset(const RecordDecl *BaseDecl,
     }
     if (CurDecl == FinalDecl)
       break;
-
-    // break;
   }
 
   assert(OffsetSum > 0);
   return OffsetSum;
 }
+
+const Record *Context::getRecord(const RecordDecl *D) const {
+  return P->getOrCreateRecord(D);
+}
diff --git a/clang/lib/AST/Interp/Context.h b/clang/lib/AST/Interp/Context.h
index 360e9499d0844..c78dc9a2a471e 100644
--- a/clang/lib/AST/Interp/Context.h
+++ b/clang/lib/AST/Interp/Context.h
@@ -107,6 +107,8 @@ class Context final {
   unsigned collectBaseOffset(const RecordDecl *BaseDecl,
                              const RecordDecl *DerivedDecl) const;
 
+  const Record *getRecord(const RecordDecl *D) const;
+
 private:
   /// Runs a function.
   bool Run(State &Parent, const Function *Func, APValue &Result);
diff --git a/clang/lib/AST/Interp/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp
index 746b765ca4216..d20ab1340c890 100644
--- a/clang/lib/AST/Interp/Descriptor.cpp
+++ b/clang/lib/AST/Interp/Descriptor.cpp
@@ -11,6 +11,7 @@
 #include "Floating.h"
 #include "FunctionPointer.h"
 #include "IntegralAP.h"
+#include "MemberPointer.h"
 #include "Pointer.h"
 #include "PrimType.h"
 #include "Record.h"
diff --git a/clang/lib/AST/Interp/Disasm.cpp b/clang/lib/AST/Interp/Disasm.cpp
index ccdc96a79436d..e3d1657532a39 100644
--- a/clang/lib/AST/Interp/Disasm.cpp
+++ b/clang/lib/AST/Interp/Disasm.cpp
@@ -17,6 +17,7 @@
 #include "Integral.h"
 #include "IntegralAP.h"
 #include "InterpFrame.h"
+#include "MemberPointer.h"
 #include "Opcode.h"
 #include "PrimType.h"
 #include "Program.h"
@@ -120,6 +121,8 @@ static const char *primTypeToString(PrimType T) {
     return "Ptr";
   case PT_FnPtr:
     return "FnPtr";
+  case PT_MemberPtr:
+    return "MemberPtr";
   }
   llvm_unreachable("Unhandled PrimType");
 }
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 145fa65791da2..41bbaf83b11c8 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -373,6 +373,26 @@ bool CheckSubobject(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
   return false;
 }
 
+bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+                   uint32_t Offset) {
+  uint32_t MinOffset = Ptr.getDeclDesc()->getMetadataSize();
+  uint32_t PtrOffset = Ptr.getByteOffset();
+
+  // We subtract Offset from PtrOffset. The result must be at least
+  // MinOffset.
+  if (Offset < PtrOffset && (PtrOffset - Offset) >= MinOffset)
+    return true;
+
+  const auto *E = cast<CastExpr>(S.Current->getExpr(OpPC));
+  QualType TargetQT = E->getType()->getPointeeType();
+  QualType MostDerivedQT = Ptr.getDeclPtr().getType();
+
+  S.CCEDiag(E, diag::note_constexpr_invalid_downcast)
+      << MostDerivedQT << TargetQT;
+
+  return false;
+}
+
 bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
   assert(Ptr.isLive() && "Pointer is not live");
   if (!Ptr.isConst())
@@ -493,10 +513,12 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
 bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
   if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
     return false;
-  if (!CheckExtern(S, OpPC, Ptr))
-    return false;
-  if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
-    return false;
+  if (!Ptr.isDummy()) {
+    if (!CheckExtern(S, OpPC, Ptr))
+      return false;
+    if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
+      return false;
+  }
   return true;
 }
 
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index eca1792e64718..17f05548a190f 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -20,6 +20,7 @@
 #include "InterpFrame.h"
 #include "InterpStack.h"
 #include "InterpState.h"
+#include "MemberPointer.h"
 #include "Opcode.h"
 #include "PrimType.h"
 #include "Program.h"
@@ -75,6 +76,11 @@ bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
 bool CheckSubobject(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                     CheckSubobjectKind CSK);
 
+/// Checks if the dowcast using the given offset is possible with the given
+/// pointer.
+bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+                   uint32_t Offset);
+
 /// Checks if a pointer points to const storage.
 bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
 
@@ -725,6 +731,9 @@ using CompareFn = llvm::function_ref<bool(ComparisonCategoryResult)>;
 
 template <typename T>
 bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) {
+  assert((!std::is_same_v<T, MemberPointer>) &&
+         "Non-equality comparisons on member pointer types should already be "
+         "rejected in Sema.");
   using BoolT = PrimConv<PT_Bool>::T;
   const T &RHS = S.Stk.pop<T>();
   const T &LHS = S.Stk.pop<T>();
@@ -834,6 +843,47 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
   }
 }
 
+template <>
+inline bool CmpHelperEQ<MemberPointer>(InterpState &S, CodePtr OpPC,
+                                       CompareFn Fn) {
+  const auto &RHS = S.Stk.pop<MemberPointer>();
+  const auto &LHS = S.Stk.pop<MemberPointer>();
+
+  // If either operand is a pointer to a weak function, the comparison is not
+  // constant.
+  for (const auto &MP : {LHS, RHS}) {
+    if (const CXXMethodDecl *MD = MP.getMemberFunction(); MD && MD->isWeak()) {
+      const SourceInfo &Loc = S.Current->getSource(OpPC);
+      S.FFDiag(Loc, diag::note_constexpr_mem_pointer_weak_comparison) << MD;
+      return false;
+    }
+  }
+
+  // C++11 [expr.eq]p2:
+  //   If both operands are null, they compare equal. Otherwise if only one is
+  //   null, they compare unequal.
+  if (LHS.isZero() && RHS.isZero()) {
+    S.Stk.push<Boolean>(Fn(ComparisonCategoryResult::Equal));
+    return true;
+  }
+  if (LHS.isZero() || RHS.isZero()) {
+    S.Stk.push<Boolean>(Fn(ComparisonCategoryResult::Unordered));
+    return true;
+  }
+
+  // We cannot compare against virtual declarations at compile time.
+  for (const auto &MP : {LHS, RHS}) {
+    if (const CXXMethodDecl *MD = MP.getMemberFunction();
+        MD && MD->isVirtual()) {
+      const SourceInfo &Loc = S.Current->getSource(OpPC);
+      S.CCEDiag(Loc, diag::note_constexpr_compare_virtual_mem_ptr) << MD;
+    }
+  }
+
+  S.Stk.push<Boolean>(Boolean::from(Fn(LHS.compare(RHS))));
+  return true;
+}
+
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool EQ(InterpState &S, CodePtr OpPC) {
   return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) {
@@ -1300,6 +1350,9 @@ inline bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off) {
     return false;
   if (!CheckSubobject(S, OpPC, Ptr, CSK_Derived))
     return false;
+  if (!CheckDowncast(S, OpPC, Ptr, Off))
+    return false;
+
   S.Stk.push<Pointer>(Ptr.atFieldSub(Off));
   return true;
 }
@@ -1324,6 +1377,12 @@ 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;
@@ -1532,6 +1591,24 @@ inline bool Memcpy(InterpState &S, CodePtr OpPC) {
   return DoMemcpy(S, OpPC, Src, Dest);
 }
 
+inline bool ToMemberPtr(InterpState &S, CodePtr OpPC) {
+  const auto &Member = S.Stk.pop<MemberPointer>();
+  const auto &Base = S.Stk.pop<Pointer>();
+
+  S.Stk.push<MemberPointer>(Member.takeInstance(Base));
+  return true;
+}
+
+inline bool CastMemberPtrPtr(InterpState &S, CodePtr OpPC) {
+  const auto &MP = S.Stk.pop<MemberPointer>();
+
+  if (std::optional<Pointer> Ptr = MP.toPointer(S.Ctx)) {
+    S.Stk.push<Pointer>(*Ptr);
+    return true;
+  }
+  return false;
+}
+
 //===----------------------------------------------------------------------===//
 // AddOffset, SubOffset
 //===----------------------------------------------------------------------===//
@@ -2329,6 +2406,28 @@ inline bool GetIntPtr(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
   return true;
 }
 
+inline bool GetMemberPtr(InterpState &S, CodePtr OpPC, const Decl *D) {
+  S.Stk.push<MemberPointer>(D);
+  return true;
+}
+
+inline bool GetMemberPtrBase(InterpState &S, CodePtr OpPC) {
+  const auto &MP = S.Stk.pop<MemberPointer>();
+
+  S.Stk.push<Pointer>(MP.getBase());
+  return true;
+}
+
+inline bool GetMemberPtrDecl(InterpState &S, CodePtr OpPC) {
+  const auto &MP = S.Stk.pop<MemberPointer>();
+
+  const auto *FD = cast<FunctionDecl>(MP.getDecl());
+  const auto *Func = S.getContext().getOrCreateFunction(FD);
+
+  S.Stk.push<FunctionPointer>(Func);
+  return true;
+}
+
 /// Just emit a diagnostic. The expression that caused emission of this
 /// op is not valid in a constant context.
 inline bool Invalid(InterpState &S, CodePtr OpPC) {
diff --git a/clang/lib/AST/Interp/InterpFrame.cpp b/clang/lib/AST/Interp/InterpFrame.cpp
index 51b0bd5c15515..54ccf9034c7a7 100644
--- a/clang/lib/AST/Interp/InterpFrame.cpp
+++ b/clang/lib/AST/Interp/InterpFrame.cpp
@@ -12,6 +12,7 @@
 #include "Function.h"
 #include "InterpStack.h"
 #include "InterpState.h"
+#include "MemberPointer.h"
 #include "Pointer.h"
 #include "PrimType.h"
 #include "Program.h"
diff --git a/clang/lib/AST/Interp/InterpStack.cpp b/clang/lib/AST/Interp/InterpStack.cpp
index 91fe40feb7671..c7024740d322e 100644
--- a/clang/lib/AST/Interp/InterpStack.cpp
+++ b/clang/lib/AST/Interp/InterpStack.cpp
@@ -10,6 +10,7 @@
 #include "Boolean.h"
 #include "Floating.h"
 #include "Integral.h"
+#include "MemberPointer.h"
 #include "Pointer.h"
 #include <cassert>
 #include <cstdlib>
diff --git a/clang/lib/AST/Interp/InterpStack.h b/clang/lib/AST/Interp/InterpStack.h
index 3fd0f63c781fc..9d85503b851be 100644
--- a/clang/lib/AST/Interp/InterpStack.h
+++ b/clang/lib/AST/Interp/InterpStack.h
@@ -15,6 +15,7 @@
 
 #include "FunctionPointer.h"
 #include "IntegralAP.h"
+#include "MemberPointer.h"
 #include "PrimType.h"
 #include <memory>
 #include <vector>
@@ -188,6 +189,8 @@ class InterpStack final {
       return PT_IntAP;
     else if constexpr (std::is_same_v<T, IntegralAP<false>>)
       return PT_IntAP;
+    else if constexpr (std::is_same_v<T, MemberPointer>)
+      return PT_MemberPtr;
 
     llvm_unreachable("unknown type push()'ed into InterpStack");
   }
diff --git a/clang/lib/AST/Interp/MemberPointer.cpp b/clang/lib/AST/Interp/MemberPointer.cpp
new file mode 100644
index 0000000000000..976472b2871b8
--- /dev/null
+++ b/clang/lib/AST/Interp/MemberPointer.cpp
@@ -0,0 +1,73 @@
+//===------------------------- MemberPointer.cpp ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MemberPointer.h"
+#include "Context.h"
+#include "FunctionPointer.h"
+#include "Program.h"
+#include "Record.h"
+
+namespace clang {
+namespace interp {
+
+std::optional<Pointer> MemberPointer::toPointer(const Context &Ctx) const {
+  if (!Dcl || isa<FunctionDecl>(Dcl))
+    return Base;
+  const FieldDecl *FD = cast<FieldDecl>(Dcl);
+  assert(FD);
+
+  Pointer CastedBase =
+      (PtrOffset < 0 ? Base.atField(-PtrOffset) : Base.atFieldSub(PtrOffset));
+
+  const Record *BaseRecord = CastedBase.getRecord();
+  if (!BaseRecord)
+    return std::nullopt;
+
+  assert(BaseRecord);
+  if (FD->getParent() == BaseRecord->getDecl())
+    return CastedBase.atField(BaseRecord->getField(FD)->Offset);
+
+  const RecordDecl *FieldParent = FD->getParent();
+  const Record *FieldRecord = Ctx.getRecord(FieldParent);
+
+  unsigned Offset = 0;
+  Offset += FieldRecord->getField(FD)->Offset;
+  Offset += CastedBase.block()->getDescriptor()->getMetadataSize();
+
+  if (Offset > CastedBase.block()->getSize())
+    return std::nullopt;
+
+  if (const RecordDecl *BaseDecl = Base.getDeclPtr().getRecord()->getDecl();
+      BaseDecl != FieldParent)
+    Offset += Ctx.collectBaseOffset(FieldParent, BaseDecl);
+
+  if (Offset > CastedBase.block()->getSize())
+    return std::nullopt;
+
+  assert(Offset <= CastedBase.block()->getSize());
+  return Pointer(const_cast<Block *>(Base.block()), Offset, Offset);
+}
+
+FunctionPointer MemberPointer::toFunctionPointer(const Context &Ctx) const {
+  return FunctionPointer(Ctx.getProgram().getFunction(cast<FunctionDecl>(Dcl)));
+}
+
+APValue MemberPointer::toAPValue() const {
+  if (isZero())
+    return APValue(static_cast<ValueDecl *>(nullptr), /*IsDerivedMember=*/false,
+                   /*Path=*/{});
+
+  if (hasBase())
+    return Base.toAPValue();
+
+  return APValue(cast<ValueDecl>(getDecl()), /*IsDerivedMember=*/false,
+                 /*Path=*/{});
+}
+
+} // namespace interp
+} // namespace clang
diff --git a/clang/lib/AST/Interp/MemberPointer.h b/clang/lib/AST/Interp/MemberPointer.h
new file mode 100644
index 0000000000000..a7551678eec20
--- /dev/null
+++ b/clang/lib/AST/Interp/MemberPointer.h
@@ -0,0 +1,112 @@
+//===------------------------- MemberPointer.h ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_MEMBER_POINTER_H
+#define LLVM_CLANG_AST_INTERP_MEMBER_POINTER_H
+
+#include "Pointer.h"
+#include <optional>
+
+namespace clang {
+class ASTContext;
+namespace interp {
+
+class Context;
+class FunctionPointer;
+
+class MemberPointer final {
+private:
+  Pointer Base;
+  const Decl *Dcl = nullptr;
+  int32_t PtrOffset = 0;
+
+  MemberPointer(Pointer Base, const Decl *Dcl, int32_t PtrOffset)
+      : Base(Base), Dcl(Dcl), PtrOffset(PtrOffset) {}
+
+public:
+  MemberPointer() = default;
+  MemberPointer(Pointer Base, const Decl *Dcl) : Base(Base), Dcl(Dcl) {}
+  MemberPointer(uint32_t Address, const Descriptor *D) {
+    // This should be impossible to hit, at least I've been unable
+    // to write a test for it.
+  }
+
+  MemberPointer(const Decl *D) : Dcl(D) {
+    assert(isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D) ||
+           isa<CXXMethodDecl>(D));
+  }
+
+  uint64_t getIntegerRepresentation() const {
+    // This should be impossible to hit, at least I've been unable
+    // to write a test for it.
+    return 17;
+  }
+
+  std::optional<Pointer> toPointer(const Context &Ctx) const;
+
+  FunctionPointer toFunctionPointer(const Context &Ctx) const;
+
+  Pointer getBase() const {
+    if (PtrOffset < 0)
+      return Base.atField(-PtrOffset);
+    return Base.atFieldSub(PtrOffset);
+  }
+  bool isMemberFunctionPointer() const {
+    return isa_and_nonnull<CXXMethodDecl>(Dcl);
+  }
+  const CXXMethodDecl *getMemberFunction() const {
+    return dyn_cast_if_present<CXXMethodDecl>(Dcl);
+  }
+  const FieldDecl *getField() const {
+    return dyn_cast_if_present<FieldDecl>(Dcl);
+  }
+
+  bool hasDecl() const { return Dcl; }
+  const Decl *getDecl() const { return Dcl; }
+
+  MemberPointer atInstanceBase(unsigned Offset) const {
+    if (Base.isZero())
+      return MemberPointer(Base, Dcl, Offset);
+    return MemberPointer(this->Base, Dcl, Offset + PtrOffset);
+  }
+
+  MemberPointer takeInstance(Pointer Instance) const {
+    assert(this->Base.isZero());
+    return MemberPointer(Instance, this->Dcl, this->PtrOffset);
+  }
+
+  APValue toAPValue() const;
+
+  bool isZero() const { return Base.isZero() && !Dcl; }
+  bool hasBase() const { return !Base.isZero(); }
+
+  void print(llvm::raw_ostream &OS) const {
+    OS << "MemberPtr(" << Base << " " << (void *)Dcl << " + " << PtrOffset
+       << ")";
+  }
+
+  std::string toDiagnosticString(const ASTContext &Ctx) const {
+    return "FIXME";
+  }
+
+  ComparisonCategoryResult compare(const MemberPointer &RHS) const {
+    if (this->Dcl == RHS.Dcl)
+      return ComparisonCategoryResult::Equal;
+    return ComparisonCategoryResult::Unordered;
+  }
+};
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, MemberPointer FP) {
+  FP.print(OS);
+  return OS;
+}
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index cfbd7f93c32de..cb4f299c8d515 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -30,6 +30,7 @@ def IntAPS : Type;
 def Float : Type;
 def Ptr : Type;
 def FnPtr : Type;
+def MemberPtr : Type;
 
 //===----------------------------------------------------------------------===//
 // Types transferred to the interpreter.
@@ -61,6 +62,7 @@ def ArgOffsetOfExpr : ArgType { let Name = "const OffsetOfExpr *"; }
 def ArgDeclRef : ArgType { let Name = "const DeclRefExpr *"; }
 def ArgDesc : ArgType { let Name = "const Descriptor *"; }
 def ArgCCI : ArgType { let Name = "const ComparisonCategoryInfo *"; }
+def ArgDecl : ArgType { let Name = "const Decl*"; }
 
 //===----------------------------------------------------------------------===//
 // Classes of types instructions operate on.
@@ -93,7 +95,7 @@ def AluTypeClass : TypeClass {
 }
 
 def PtrTypeClass : TypeClass {
-  let Types = [Ptr, FnPtr];
+  let Types = [Ptr, FnPtr, MemberPtr];
 }
 
 def BoolTypeClass : TypeClass {
@@ -208,7 +210,6 @@ def CallBI : Opcode {
 
 def CallPtr : Opcode {
   let Args = [ArgUint32, ArgCallExpr];
-  let Types = [];
 }
 
 def CallVar : Opcode {
@@ -327,6 +328,11 @@ def GetPtrBasePop : Opcode {
   // Offset of field, which is a base.
   let Args = [ArgUint32];
 }
+def GetMemberPtrBasePop : Opcode {
+  // Offset of field, which is a base.
+  let Args = [ArgSint32];
+}
+
 
 def FinishInitPop : Opcode;
 def FinishInit    : Opcode;
@@ -751,6 +757,14 @@ def CheckNonNullArg : Opcode {
 
 def Memcpy : Opcode;
 
+def ToMemberPtr : Opcode;
+def CastMemberPtrPtr : Opcode;
+def GetMemberPtr : Opcode {
+  let Args = [ArgDecl];
+}
+def GetMemberPtrBase : Opcode;
+def GetMemberPtrDecl : Opcode;
+
 //===----------------------------------------------------------------------===//
 // Debugging.
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp
index 252f7ea46086f..a60b4d28b4387 100644
--- a/clang/lib/AST/Interp/Pointer.cpp
+++ b/clang/lib/AST/Interp/Pointer.cpp
@@ -13,6 +13,7 @@
 #include "Function.h"
 #include "Integral.h"
 #include "InterpBlock.h"
+#include "MemberPointer.h"
 #include "PrimType.h"
 #include "Record.h"
 
diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h
index 93ca754d04a64..c6e4f4d0b4abd 100644
--- a/clang/lib/AST/Interp/Pointer.h
+++ b/clang/lib/AST/Interp/Pointer.h
@@ -620,6 +620,7 @@ class Pointer {
 private:
   friend class Block;
   friend class DeadBlock;
+  friend class MemberPointer;
   friend struct InitMap;
 
   Pointer(Block *Pointee, unsigned Base, uint64_t Offset);
diff --git a/clang/lib/AST/Interp/PrimType.cpp b/clang/lib/AST/Interp/PrimType.cpp
index 9b96dcfe6a272..3054e67d5c49f 100644
--- a/clang/lib/AST/Interp/PrimType.cpp
+++ b/clang/lib/AST/Interp/PrimType.cpp
@@ -11,6 +11,7 @@
 #include "Floating.h"
 #include "FunctionPointer.h"
 #include "IntegralAP.h"
+#include "MemberPointer.h"
 #include "Pointer.h"
 
 using namespace clang;
diff --git a/clang/lib/AST/Interp/PrimType.h b/clang/lib/AST/Interp/PrimType.h
index 05a094d0c5b1f..9e23947fe6385 100644
--- a/clang/lib/AST/Interp/PrimType.h
+++ b/clang/lib/AST/Interp/PrimType.h
@@ -25,6 +25,7 @@ class Pointer;
 class Boolean;
 class Floating;
 class FunctionPointer;
+class MemberPointer;
 template <bool Signed> class IntegralAP;
 template <unsigned Bits, bool Signed> class Integral;
 
@@ -44,10 +45,11 @@ enum PrimType : unsigned {
   PT_Float,
   PT_Ptr,
   PT_FnPtr,
+  PT_MemberPtr,
 };
 
 inline constexpr bool isPtrType(PrimType T) {
-  return T == PT_Ptr || T == PT_FnPtr;
+  return T == PT_Ptr || T == PT_FnPtr || T == PT_MemberPtr;
 }
 
 enum class CastKind : uint8_t {
@@ -91,6 +93,9 @@ template <> struct PrimConv<PT_Ptr> { using T = Pointer; };
 template <> struct PrimConv<PT_FnPtr> {
   using T = FunctionPointer;
 };
+template <> struct PrimConv<PT_MemberPtr> {
+  using T = MemberPointer;
+};
 
 /// Returns the size of a primitive type in bytes.
 size_t primSize(PrimType Type);
@@ -131,6 +136,7 @@ static inline bool aligned(const void *P) {
       TYPE_SWITCH_CASE(PT_Bool, B)                                             \
       TYPE_SWITCH_CASE(PT_Ptr, B)                                              \
       TYPE_SWITCH_CASE(PT_FnPtr, B)                                            \
+      TYPE_SWITCH_CASE(PT_MemberPtr, B)                                        \
     }                                                                          \
   } while (0)
 
diff --git a/clang/test/AST/Interp/eval-order.cpp b/clang/test/AST/Interp/eval-order.cpp
index aaf2b74510bbf..7a7ce6a714601 100644
--- a/clang/test/AST/Interp/eval-order.cpp
+++ b/clang/test/AST/Interp/eval-order.cpp
@@ -71,8 +71,8 @@ namespace EvalOrder {
   // Rules 1 and 2 have no effect ('b' is not an expression).
 
   // Rule 3: a->*b
-  // SEQ(A(ud).*B(&UserDefined::n)); FIXME
-  // SEQ(A(&ud)->*B(&UserDefined::n)); FIXME
+  SEQ(A(ud).*B(&UserDefined::n));
+  SEQ(A(&ud)->*B(&UserDefined::n));
 
   // Rule 4: a(b1, b2, b3)
   SEQ(A(f)(B(1), B(2), B(3))); // expected-error {{not an integral constant expression}} FIXME \
diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp
index c160be06dd241..27b2af7db0da0 100644
--- a/clang/test/AST/Interp/literals.cpp
+++ b/clang/test/AST/Interp/literals.cpp
@@ -66,7 +66,12 @@ namespace ScalarTypes {
     First = 0,
   };
   static_assert(getScalar<E>() == First, "");
-  /// FIXME: Member pointers.
+
+  struct S {
+    int v;
+  };
+  constexpr int S::* MemberPtr = &S::v;
+  static_assert(getScalar<decltype(MemberPtr)>() == nullptr, "");
 
 #if __cplusplus >= 201402L
   constexpr void Void(int n) {
diff --git a/clang/test/AST/Interp/memberpointers.cpp b/clang/test/AST/Interp/memberpointers.cpp
new file mode 100644
index 0000000000000..2ee92c1f07708
--- /dev/null
+++ b/clang/test/AST/Interp/memberpointers.cpp
@@ -0,0 +1,184 @@
+// RUN: %clang_cc1 -std=c++14 -fexperimental-new-constant-interpreter -verify=expected,both %s
+// RUN: %clang_cc1 -std=c++14 -verify=ref,both %s
+
+namespace MemberPointers {
+  struct A {
+    constexpr A(int n) : n(n) {}
+    int n;
+    constexpr int f() const { return n + 3; }
+  };
+
+  constexpr A a(7);
+  static_assert(A(5).*&A::n == 5, "");
+  static_assert((&a)->*&A::n == 7, "");
+  static_assert((A(8).*&A::f)() == 11, "");
+  static_assert(((&a)->*&A::f)() == 10, "");
+
+  struct B : A {
+    constexpr B(int n, int m) : A(n), m(m) {}
+    int m;
+    constexpr int g() const { return n + m + 1; }
+  };
+  constexpr B b(9, 13);
+  static_assert(B(4, 11).*&A::n == 4, "");
+  static_assert(B(4, 11).*&B::m == 11, "");
+  static_assert(B(4, 11).m == 11, "");
+  static_assert(B(4, 11).*(int(A::*))&B::m == 11, "");
+  static_assert(B(4, 11).*&B::m == 11, "");
+  static_assert((&b)->*&A::n == 9, "");
+  static_assert((&b)->*&B::m == 13, "");
+  static_assert((&b)->*(int(A::*))&B::m == 13, "");
+  static_assert((B(4, 11).*&A::f)() == 7, "");
+  static_assert((B(4, 11).*&B::g)() == 16, "");
+
+  static_assert((B(4, 11).*(int(A::*)() const)&B::g)() == 16, "");
+
+  static_assert(((&b)->*&A::f)() == 12, "");
+  static_assert(((&b)->*&B::g)() == 23, "");
+  static_assert(((&b)->*(int(A::*)()const)&B::g)() == 23, "");
+
+
+  struct S {
+    constexpr S(int m, int n, int (S::*pf)() const, int S::*pn) :
+      m(m), n(n), pf(pf), pn(pn) {}
+    constexpr S() : m(), n(), pf(&S::f), pn(&S::n) {}
+
+    constexpr int f() const { return this->*pn; }
+    virtual int g() const;
+
+    int m, n;
+    int (S::*pf)() const;
+    int S::*pn;
+  };
+
+  constexpr int S::*pm = &S::m;
+  constexpr int S::*pn = &S::n;
+
+  constexpr int (S::*pf)() const = &S::f;
+  constexpr int (S::*pg)() const = &S::g;
+
+  constexpr S s(2, 5, &S::f, &S::m);
+
+  static_assert((s.*&S::f)() == 2, "");
+  static_assert((s.*s.pf)() == 2, "");
+
+  static_assert(pf == &S::f, "");
+
+  static_assert(pf == s.*&S::pf, "");
+
+  static_assert(pm == &S::m, "");
+  static_assert(pm != pn, "");
+  static_assert(s.pn != pn, "");
+  static_assert(s.pn == pm, "");
+  static_assert(pg != nullptr, "");
+  static_assert(pf != nullptr, "");
+  static_assert((int S::*)nullptr == nullptr, "");
+  static_assert(pg == pg, ""); // both-error {{constant expression}} \
+                               // both-note {{comparison of pointer to virtual member function 'g' has unspecified value}}
+  static_assert(pf != pg, ""); // both-error {{constant expression}} \
+                               // both-note {{comparison of pointer to virtual member function 'g' has unspecified value}}
+
+  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; };
+
+  T<17> t17;
+  T<30> t30;
+
+  constexpr int (T<15>::*deepm) = (int(T<10>::*))&T<30>::m;
+  constexpr int (T<10>::*deepn) = &T<0>::n;
+  constexpr char (T<10>::*deepk) = &T<0>::k;
+
+  static_assert(&(t17.*deepn) == &t17.n, "");
+  static_assert(&(t17.*deepk) == &t17.k, "");
+  static_assert(deepn == &T<2>::n, "");
+
+  constexpr int *pgood = &(t30.*deepm);
+  constexpr int *pbad = &(t17.*deepm); // both-error {{constant expression}}
+  static_assert(&(t30.*deepm) == &t30.m, "");
+
+  static_assert(deepm == &T<50>::m, "");
+  static_assert(deepm != deepn, "");
+
+  constexpr T<5> *p17_5 = &t17;
+  constexpr T<13> *p17_13 = (T<13>*)p17_5;
+  constexpr T<23> *p17_23 = (T<23>*)p17_13; // both-error {{constant expression}} \
+                                            // both-note {{cannot cast object of dynamic type 'T<17>' to type 'T<23>'}}
+  constexpr T<18> *p17_18 = (T<18>*)p17_13; // both-error {{constant expression}} \
+                                            // both-note {{cannot cast object of dynamic type 'T<17>' to type 'T<18>'}}
+  static_assert(&(p17_5->*(int(T<0>::*))deepn) == &t17.n, "");
+  static_assert(&(p17_5->*(int(T<0>::*))deepn), "");
+
+
+  static_assert(&(p17_13->*deepn) == &t17.n, "");
+  constexpr int *pbad2 = &(p17_13->*(int(T<9>::*))deepm); // both-error {{constant expression}}
+
+  constexpr T<5> *p30_5 = &t30;
+  constexpr T<23> *p30_23 = (T<23>*)p30_5;
+  constexpr T<13> *p30_13 = p30_23;
+  static_assert(&(p30_13->*deepn) == &t30.n, "");
+  static_assert(&(p30_23->*deepn) == &t30.n, "");
+  static_assert(&(p30_5->*(int(T<3>::*))deepn) == &t30.n, "");
+
+  static_assert(&(p30_5->*(int(T<2>::*))deepm) == &t30.m, "");
+  static_assert(&(((T<17>*)p30_13)->*deepm) == &t30.m, "");
+  static_assert(&(p30_23->*deepm) == &t30.m, "");
+
+
+  /// Added tests not from constant-expression-cxx11.cpp
+  static_assert(pm, "");
+  static_assert(!((int S::*)nullptr), "");
+  constexpr int S::*pk = nullptr;
+  static_assert(!pk, "");
+}
+
+namespace test3 {
+  struct nsCSSRect {
+  };
+  static int nsCSSRect::* sides;
+  nsCSSRect dimenX;
+  void ParseBoxCornerRadii(int y) {
+    switch (y) {
+    }
+    int& x = dimenX.*sides;
+  }
+}
+
+void foo() {
+  class X;
+  void (X::*d) ();
+  d = nullptr; /// This calls in the constant interpreter.
+}
+
+namespace {
+  struct A { int n; };
+  struct B { int n; };
+  struct C : A, B {};
+  struct D { double d; C c; };
+  const int &&u = static_cast<B&&>(0, ((D&&)D{}).*&D::c).n; // both-warning {{left operand of comma operator has no effect}}
+}
+
+/// From SemaTemplate/instantiate-member-pointers.cpp
+namespace {
+  struct Y {
+    int x;
+  };
+
+  template<typename T, typename Class, T Class::*Ptr>
+  struct X3 {
+    X3<T, Class, Ptr> &operator=(const T& value) {
+      return *this;
+    }
+  };
+
+  typedef int Y::*IntMember;
+  template<IntMember Member>
+  struct X4 {
+    X3<int, Y, Member> member;
+    int &getMember(Y& y) { return y.*Member; }
+  };
+
+  int &get_X4(X4<&Y::x> x4, Y& y) {
+    return x4.getMember(y);
+  }
+}
diff --git a/clang/test/CodeGenCXX/mangle-ms-templates-memptrs.cpp b/clang/test/CodeGenCXX/mangle-ms-templates-memptrs.cpp
index 3078c5a5d2683..bc6d9d3215fb0 100644
--- a/clang/test/CodeGenCXX/mangle-ms-templates-memptrs.cpp
+++ b/clang/test/CodeGenCXX/mangle-ms-templates-memptrs.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -Wno-microsoft -fno-rtti -std=c++11 -emit-llvm %s -o - -triple=i386-pc-win32 | FileCheck %s
+// RUN: %clang_cc1 -Wno-microsoft -fno-rtti -std=c++11 -emit-llvm %s -o - -triple=i386-pc-win32 -fexperimental-new-constant-interpreter | FileCheck %s
 
 struct U;
 static_assert(sizeof(void (U::*)()) == 2 * sizeof(void*) + 2 * sizeof(int), "");
diff --git a/clang/test/CodeGenCXX/pointers-to-data-members.cpp b/clang/test/CodeGenCXX/pointers-to-data-members.cpp
index 29f1c3f48e3ac..3d4495212e6e4 100644
--- a/clang/test/CodeGenCXX/pointers-to-data-members.cpp
+++ b/clang/test/CodeGenCXX/pointers-to-data-members.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -o %t.ll -triple=x86_64-apple-darwin10
+// RUN: %clang_cc1 %s -emit-llvm -o %t.ll -triple=x86_64-apple-darwin10 -fexperimental-new-constant-interpreter
 // RUN: FileCheck %s < %t.ll
 // RUN: FileCheck -check-prefix=CHECK-GLOBAL %s < %t.ll
 
diff --git a/clang/test/SemaCXX/attr-weak.cpp b/clang/test/SemaCXX/attr-weak.cpp
index f065bfd9483f8..0f9a2975e5f68 100644
--- a/clang/test/SemaCXX/attr-weak.cpp
+++ b/clang/test/SemaCXX/attr-weak.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -verify -std=c++11 %s -fexperimental-new-constant-interpreter
 
 static int test0 __attribute__((weak)); // expected-error {{weak declaration cannot have internal linkage}}
 static void test1() __attribute__((weak)); // expected-error {{weak declaration cannot have internal linkage}}
diff --git a/clang/test/SemaCXX/nullptr_in_arithmetic_ops.cpp b/clang/test/SemaCXX/nullptr_in_arithmetic_ops.cpp
index 6273d9c42e0b1..df8bbcc0eda5d 100644
--- a/clang/test/SemaCXX/nullptr_in_arithmetic_ops.cpp
+++ b/clang/test/SemaCXX/nullptr_in_arithmetic_ops.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -Wno-tautological-pointer-compare -fblocks -std=c++11 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wno-tautological-pointer-compare -fblocks -std=c++11 -verify %s -fexperimental-new-constant-interpreter
 
 void foo() {
   int a;
diff --git a/clang/unittests/AST/Interp/toAPValue.cpp b/clang/unittests/AST/Interp/toAPValue.cpp
index e56453aba2c5f..d6879d6e0bca3 100644
--- a/clang/unittests/AST/Interp/toAPValue.cpp
+++ b/clang/unittests/AST/Interp/toAPValue.cpp
@@ -186,3 +186,49 @@ TEST(ToAPValue, FunctionPointersC) {
     ASSERT_EQ(I, 17);
   }
 }
+
+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";
+
+  auto AST = tooling::buildASTFromCodeWithArgs(
+      Code, {"-fexperimental-new-constant-interpreter"});
+
+  auto &Ctx = AST->getASTContext().getInterpContext();
+  Program &Prog = Ctx.getProgram();
+
+  auto getDecl = [&](const char *Name) -> const ValueDecl * {
+    auto Nodes =
+        match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
+    assert(Nodes.size() == 1);
+    const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
+    assert(D);
+    return D;
+  };
+
+  auto getGlobalPtr = [&](const char *Name) -> Pointer {
+    const VarDecl *D = cast<VarDecl>(getDecl(Name));
+    return Prog.getPtrGlobal(*Prog.getGlobal(D));
+  };
+
+  {
+    const Pointer &GP = getGlobalPtr("pm");
+    ASSERT_TRUE(GP.isLive());
+    const MemberPointer &FP = GP.deref<MemberPointer>();
+    APValue A = FP.toAPValue();
+    ASSERT_EQ(A.getMemberPointerDecl(), getDecl("m"));
+    ASSERT_EQ(A.getKind(), APValue::MemberPointer);
+  }
+
+  {
+    const Pointer &GP = getGlobalPtr("nn");
+    ASSERT_TRUE(GP.isLive());
+    const MemberPointer &NP = GP.deref<MemberPointer>();
+    ASSERT_TRUE(NP.isZero());
+    APValue A = NP.toAPValue();
+    ASSERT_EQ(A.getKind(), APValue::MemberPointer);
+  }
+}

>From 55b6ad83ecd3fd089941500ded65d8a47f093775 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sat, 25 May 2024 06:41:13 +0200
Subject: [PATCH 2/7] Address review feedback

---
 clang/lib/AST/Interp/ByteCodeExprGen.cpp | 3 +--
 clang/lib/AST/Interp/MemberPointer.h     | 4 ++--
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 5f8b94c3a0f94..53e6b4cfc417a 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -3348,8 +3348,7 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
         return false;
       if (!this->emitGetMemberPtrBase(E))
         return false;
-    } else {
-      if (!this->visit(MC->getImplicitObjectArgument()))
+    } else if (!this->visit(MC->getImplicitObjectArgument())) {
         return false;
     }
   }
diff --git a/clang/lib/AST/Interp/MemberPointer.h b/clang/lib/AST/Interp/MemberPointer.h
index a7551678eec20..d5299e0ff15c6 100644
--- a/clang/lib/AST/Interp/MemberPointer.h
+++ b/clang/lib/AST/Interp/MemberPointer.h
@@ -34,6 +34,7 @@ class MemberPointer final {
   MemberPointer(uint32_t Address, const Descriptor *D) {
     // This should be impossible to hit, at least I've been unable
     // to write a test for it.
+    assert(false && "This constructor shouldn't be reachable for MemberPointers");
   }
 
   MemberPointer(const Decl *D) : Dcl(D) {
@@ -42,8 +43,7 @@ class MemberPointer final {
   }
 
   uint64_t getIntegerRepresentation() const {
-    // This should be impossible to hit, at least I've been unable
-    // to write a test for it.
+    assert(false && "getIntegerRepresentation() shouldn't be reachable for MemberPointers");
     return 17;
   }
 

>From d76c5855a2529c5849835043ef7d9d6a55280f8e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sat, 25 May 2024 20:38:02 +0200
Subject: [PATCH 3/7] Allow MemberPointer(int, const Descriptor*), but only for
 Address==0

---
 clang/lib/AST/Interp/MemberPointer.h | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/clang/lib/AST/Interp/MemberPointer.h b/clang/lib/AST/Interp/MemberPointer.h
index d5299e0ff15c6..10ecc1d5ddf7f 100644
--- a/clang/lib/AST/Interp/MemberPointer.h
+++ b/clang/lib/AST/Interp/MemberPointer.h
@@ -32,9 +32,8 @@ class MemberPointer final {
   MemberPointer() = default;
   MemberPointer(Pointer Base, const Decl *Dcl) : Base(Base), Dcl(Dcl) {}
   MemberPointer(uint32_t Address, const Descriptor *D) {
-    // This should be impossible to hit, at least I've been unable
-    // to write a test for it.
-    assert(false && "This constructor shouldn't be reachable for MemberPointers");
+    // We only reach this for Address == 0, when creating a null member pointer.
+    assert(Address == 0);
   }
 
   MemberPointer(const Decl *D) : Dcl(D) {

>From c0f567ea4b46f70362beb23743227179bbad9a38 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sun, 26 May 2024 08:24:11 +0200
Subject: [PATCH 4/7] Fix another edge case

---
 clang/lib/AST/Interp/MemberPointer.cpp   |  3 +++
 clang/test/AST/Interp/memberpointers.cpp | 13 +++++++++++++
 2 files changed, 16 insertions(+)

diff --git a/clang/lib/AST/Interp/MemberPointer.cpp b/clang/lib/AST/Interp/MemberPointer.cpp
index 976472b2871b8..96f63643e83c9 100644
--- a/clang/lib/AST/Interp/MemberPointer.cpp
+++ b/clang/lib/AST/Interp/MemberPointer.cpp
@@ -21,6 +21,9 @@ std::optional<Pointer> MemberPointer::toPointer(const Context &Ctx) const {
   const FieldDecl *FD = cast<FieldDecl>(Dcl);
   assert(FD);
 
+  if (!Base.isBlockPointer())
+    return std::nullopt;
+
   Pointer CastedBase =
       (PtrOffset < 0 ? Base.atField(-PtrOffset) : Base.atFieldSub(PtrOffset));
 
diff --git a/clang/test/AST/Interp/memberpointers.cpp b/clang/test/AST/Interp/memberpointers.cpp
index 2ee92c1f07708..54d73fe86ca18 100644
--- a/clang/test/AST/Interp/memberpointers.cpp
+++ b/clang/test/AST/Interp/memberpointers.cpp
@@ -182,3 +182,16 @@ namespace {
     return x4.getMember(y);
   }
 }
+
+/// From test/CXX/basic/basic.def.odr/p2.cpp
+namespace {
+  void use(int);
+  struct S { int x; int f() const; };
+  constexpr S *ps = nullptr;
+  S *const &psr = ps;
+
+  void test() {
+    use(ps->*&S::x);
+    use(psr->*&S::x);
+  }
+}

>From 545c107e5c40e6ee257ca797a3e51ac82eba3051 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sun, 26 May 2024 08:47:13 +0200
Subject: [PATCH 5/7] [clang][Interp] Handle objc strings

---
 clang/lib/AST/Interp/ByteCodeExprGen.cpp | 6 ++++++
 clang/lib/AST/Interp/ByteCodeExprGen.h   | 1 +
 clang/lib/AST/Interp/Context.cpp         | 3 ++-
 clang/test/AST/Interp/objc.mm            | 4 ++++
 4 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 53e6b4cfc417a..8245182248b7b 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -1683,6 +1683,12 @@ bool ByteCodeExprGen<Emitter>::VisitStringLiteral(const StringLiteral *E) {
   return true;
 }
 
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitObjCStringLiteral(
+    const ObjCStringLiteral *E) {
+  return this->delegate(E->getString());
+}
+
 template <class Emitter>
 bool ByteCodeExprGen<Emitter>::VisitSYCLUniqueStableNameExpr(
     const SYCLUniqueStableNameExpr *E) {
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index a2e283c866332..c648b0e4be704 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -90,6 +90,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
   bool VisitOpaqueValueExpr(const OpaqueValueExpr *E);
   bool VisitAbstractConditionalOperator(const AbstractConditionalOperator *E);
   bool VisitStringLiteral(const StringLiteral *E);
+  bool VisitObjCStringLiteral(const ObjCStringLiteral *E);
   bool VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E);
   bool VisitCharacterLiteral(const CharacterLiteral *E);
   bool VisitCompoundAssignOperator(const CompoundAssignOperator *E);
diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp
index a4561cb1c9595..2e4c634dd2c9d 100644
--- a/clang/lib/AST/Interp/Context.cpp
+++ b/clang/lib/AST/Interp/Context.cpp
@@ -168,7 +168,8 @@ std::optional<PrimType> Context::classify(QualType T) const {
       T->isFunctionType())
     return PT_FnPtr;
 
-  if (T->isReferenceType() || T->isPointerType())
+  if (T->isReferenceType() || T->isPointerType() ||
+      T->isObjCObjectPointerType())
     return PT_Ptr;
 
   if (const auto *AT = T->getAs<AtomicType>())
diff --git a/clang/test/AST/Interp/objc.mm b/clang/test/AST/Interp/objc.mm
index 44b74d193b66a..e48fa3c0ac709 100644
--- a/clang/test/AST/Interp/objc.mm
+++ b/clang/test/AST/Interp/objc.mm
@@ -6,3 +6,7 @@ @interface A {
   static_assert(a, ""); // both-error {{static assertion expression is not an integral constant expression}}
 }
 @end
+
+ at interface NSString
+ at end
+constexpr NSString *t0 = @"abc";

>From e9e0819b54a1cda747974ae0f9cb7f0c40b0dad3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sun, 26 May 2024 08:51:51 +0200
Subject: [PATCH 6/7] [clang][Interp] Handle ObjCBoxedExprs

---
 clang/lib/AST/Interp/ByteCodeExprGen.cpp | 8 ++++++++
 clang/lib/AST/Interp/ByteCodeExprGen.h   | 1 +
 clang/test/AST/Interp/objc.mm            | 1 +
 3 files changed, 10 insertions(+)

diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 8245182248b7b..fa7aa3bf186eb 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -2695,6 +2695,14 @@ bool ByteCodeExprGen<Emitter>::VisitShuffleVectorExpr(
   return true;
 }
 
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitObjCBoxedExpr(const ObjCBoxedExpr *E) {
+  if (!E->isExpressibleAsConstantInitializer())
+    return this->emitInvalid(E);
+
+  return this->delegate(E->getSubExpr());
+}
+
 template <class Emitter> bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
   OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/true,
                              /*NewInitializing=*/false);
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index c648b0e4be704..44c495240289f 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -127,6 +127,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
   bool VisitAddrLabelExpr(const AddrLabelExpr *E);
   bool VisitConvertVectorExpr(const ConvertVectorExpr *E);
   bool VisitShuffleVectorExpr(const ShuffleVectorExpr *E);
+  bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E);
 
 protected:
   bool visitExpr(const Expr *E) override;
diff --git a/clang/test/AST/Interp/objc.mm b/clang/test/AST/Interp/objc.mm
index e48fa3c0ac709..6402c8ae098fd 100644
--- a/clang/test/AST/Interp/objc.mm
+++ b/clang/test/AST/Interp/objc.mm
@@ -10,3 +10,4 @@ @interface A {
 @interface NSString
 @end
 constexpr NSString *t0 = @"abc";
+constexpr NSString *t1 = @("abc");

>From eb1cfe1591a32410072af1266e77227f0d737180 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sat, 1 Jun 2024 20:16:16 +0200
Subject: [PATCH 7/7] Update isa<> syntax in assert

---
 clang/lib/AST/Interp/MemberPointer.h | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/lib/AST/Interp/MemberPointer.h b/clang/lib/AST/Interp/MemberPointer.h
index 10ecc1d5ddf7f..5d9a472aa4b63 100644
--- a/clang/lib/AST/Interp/MemberPointer.h
+++ b/clang/lib/AST/Interp/MemberPointer.h
@@ -37,8 +37,7 @@ class MemberPointer final {
   }
 
   MemberPointer(const Decl *D) : Dcl(D) {
-    assert(isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D) ||
-           isa<CXXMethodDecl>(D));
+    assert((isa<FieldDecl, IndirectFieldDecl, CXXMethodDecl>(D));
   }
 
   uint64_t getIntegerRepresentation() const {



More information about the cfe-commits mailing list