[clang] [clang][Interp] Member Pointers (PR #91303)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 5 23:41:19 PDT 2024
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/91303
>From 0f047f686ac69549834bfbfdecaa6383c6ab5305 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] Memberpointers
---
clang/lib/AST/CMakeLists.txt | 1 +
clang/lib/AST/Interp/ByteCodeExprGen.cpp | 100 ++++++++-
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 | 76 +++++++
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 | 197 ++++++++++++++++++
.../CodeGenCXX/pointers-to-data-members.cpp | 1 +
clang/test/SemaCXX/attr-weak.cpp | 1 +
.../SemaCXX/nullptr_in_arithmetic_ops.cpp | 1 +
clang/unittests/AST/Interp/toAPValue.cpp | 46 ++++
25 files changed, 705 insertions(+), 25 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 3671c41ae7039..b0f28c9cec414 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());
@@ -2773,6 +2819,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);
}
@@ -2875,6 +2923,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:
@@ -3308,10 +3357,27 @@ 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()))
+ 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);
@@ -3380,11 +3446,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.
@@ -3623,6 +3700,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 b0b22b059b77a..98d1837204ebc 100644
--- a/clang/lib/AST/Interp/Context.cpp
+++ b/clang/lib/AST/Interp/Context.cpp
@@ -163,8 +163,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() ||
@@ -177,9 +181,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;
}
@@ -292,10 +293,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 3f8a92ed2f0bd..0ab84d159c58b 100644
--- a/clang/lib/AST/Interp/Disasm.cpp
+++ b/clang/lib/AST/Interp/Disasm.cpp
@@ -19,6 +19,7 @@
#include "Integral.h"
#include "IntegralAP.h"
#include "InterpFrame.h"
+#include "MemberPointer.h"
#include "Opcode.h"
#include "PrimType.h"
#include "Program.h"
@@ -122,6 +123,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..96f63643e83c9
--- /dev/null
+++ b/clang/lib/AST/Interp/MemberPointer.cpp
@@ -0,0 +1,76 @@
+//===------------------------- 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);
+
+ if (!Base.isBlockPointer())
+ return std::nullopt;
+
+ 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..5c61f6a439574
--- /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) {
+ // We only reach this for Address == 0, when creating a null member pointer.
+ assert(Address == 0);
+ }
+
+ MemberPointer(const Decl *D) : Dcl(D) {
+ assert((isa<FieldDecl, IndirectFieldDecl, CXXMethodDecl>(D)));
+ }
+
+ uint64_t getIntegerRepresentation() const {
+ assert(
+ false &&
+ "getIntegerRepresentation() shouldn't be reachable for MemberPointers");
+ 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 604fb5dfde1e4..20fb5e81774d6 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 = 11,
PT_Ptr = 12,
PT_FnPtr = 13,
+ PT_MemberPtr = 14,
};
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..54d73fe86ca18
--- /dev/null
+++ b/clang/test/AST/Interp/memberpointers.cpp
@@ -0,0 +1,197 @@
+// 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);
+ }
+}
+
+/// 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);
+ }
+}
diff --git a/clang/test/CodeGenCXX/pointers-to-data-members.cpp b/clang/test/CodeGenCXX/pointers-to-data-members.cpp
index 29f1c3f48e3ac..cf1d6c018409d 100644
--- a/clang/test/CodeGenCXX/pointers-to-data-members.cpp
+++ b/clang/test/CodeGenCXX/pointers-to-data-members.cpp
@@ -1,4 +1,5 @@
// 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..98bec184164b6 100644
--- a/clang/test/SemaCXX/nullptr_in_arithmetic_ops.cpp
+++ b/clang/test/SemaCXX/nullptr_in_arithmetic_ops.cpp
@@ -1,4 +1,5 @@
// 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);
+ }
+}
More information about the cfe-commits
mailing list