Timm =?utf-8?q?Bäder?= <tbae...@redhat.com> Message-ID: In-Reply-To: <llvm.org/llvm/llvm-project/pull/91...@github.com>
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/91303 >From df90df17e949e264f0b0f6816cd6bd138e159271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Wed, 10 Apr 2024 16:42:36 +0200 Subject: [PATCH 1/2] 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 19bb75633bf6875cecd0d6ec72e4d756c4b39174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Sat, 25 May 2024 06:41:13 +0200 Subject: [PATCH 2/2] 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; } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits