https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/121251
>From af5d2292249514be8f2d13e92f98eb0df1d7eb32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Thu, 26 Dec 2024 08:54:48 +0100 Subject: [PATCH] [clang][bytecode] Add support for typeid pointers Add it as another kind of pointer, saving both a `Type*` for the result of the typeid() expression as well as one for the type of the typeid expression. --- clang/lib/AST/ByteCode/Compiler.cpp | 32 +++++++++++ clang/lib/AST/ByteCode/Compiler.h | 1 + clang/lib/AST/ByteCode/Interp.cpp | 82 +++++++++++++++++++++++++++++ clang/lib/AST/ByteCode/Interp.h | 63 +++------------------- clang/lib/AST/ByteCode/Opcodes.td | 4 ++ clang/lib/AST/ByteCode/Pointer.cpp | 16 ++++++ clang/lib/AST/ByteCode/Pointer.h | 25 +++++++-- clang/test/AST/ByteCode/cxx2a.cpp | 60 +++++++++++++++++++++ 8 files changed, 224 insertions(+), 59 deletions(-) diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 68c75b01e6f6df..036f9608bf3ca1 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -3426,6 +3426,38 @@ bool Compiler<Emitter>::VisitBlockExpr(const BlockExpr *E) { return this->emitGetFnPtr(Func, E); } +template <class Emitter> +bool Compiler<Emitter>::VisitCXXTypeidExpr(const CXXTypeidExpr *E) { + const Type *TypeInfoType = E->getType().getTypePtr(); + + if (!E->isPotentiallyEvaluated()) { + if (DiscardResult) + return true; + + if (E->isTypeOperand()) + return this->emitGetTypeid( + E->getTypeOperand(Ctx.getASTContext()).getTypePtr(), TypeInfoType, E); + return this->emitGetTypeid(E->getExprOperand()->getType().getTypePtr(), + TypeInfoType, E); + } + + // Otherwise, we need to evaluate the expression operand. + assert(E->getExprOperand()); + assert(E->getExprOperand()->isLValue()); + + if (!Ctx.getLangOpts().CPlusPlus20 && !this->emitDiagTypeid(E)) + return false; + + if (!this->visit(E->getExprOperand())) + return false; + + if (!this->emitGetTypeidPtr(TypeInfoType, E)) + return false; + if (DiscardResult) + return this->emitPopPtr(E); + return true; +} + template <class Emitter> bool Compiler<Emitter>::VisitExpressionTraitExpr(const ExpressionTraitExpr *E) { assert(Ctx.getLangOpts().CPlusPlus); diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h index 2a94f5ec76b6c5..71765b18cb1a90 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -205,6 +205,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>, bool VisitCXXNewExpr(const CXXNewExpr *E); bool VisitCXXDeleteExpr(const CXXDeleteExpr *E); bool VisitBlockExpr(const BlockExpr *E); + bool VisitCXXTypeidExpr(const CXXTypeidExpr *E); // Statements. bool visitCompoundStmt(const CompoundStmt *S); diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 7c7752080746e9..cb0ce886f66809 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -1154,6 +1154,53 @@ bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T) { return false; } +static bool getField(InterpState &S, CodePtr OpPC, const Pointer &Ptr, + uint32_t Off) { + if (S.getLangOpts().CPlusPlus && S.inConstantContext() && + !CheckNull(S, OpPC, Ptr, CSK_Field)) + return false; + + if (!CheckExtern(S, OpPC, Ptr)) + return false; + if (!CheckRange(S, OpPC, Ptr, CSK_Field)) + return false; + if (!CheckArray(S, OpPC, Ptr)) + return false; + if (!CheckSubobject(S, OpPC, Ptr, CSK_Field)) + return false; + + if (Ptr.isIntegralPointer()) { + S.Stk.push<Pointer>(Ptr.asIntPointer().atOffset(S.getASTContext(), Off)); + return true; + } + + if (!Ptr.isBlockPointer()) { + // FIXME: The only time we (seem to) get here is when trying to access a + // field of a typeid pointer. In that case, we're supposed to diagnose e.g. + // `typeid(int).name`, but we currently diagnose `&typeid(int)`. + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_access_unreadable_object) + << AK_Read << Ptr.toDiagnosticString(S.getASTContext()); + return false; + } + + if (Off > Ptr.block()->getSize()) + return false; + + S.Stk.push<Pointer>(Ptr.atField(Off)); + return true; +} + +bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) { + const auto &Ptr = S.Stk.peek<Pointer>(); + return getField(S, OpPC, Ptr, Off); +} + +bool GetPtrFieldPop(InterpState &S, CodePtr OpPC, uint32_t Off) { + const auto &Ptr = S.Stk.pop<Pointer>(); + return getField(S, OpPC, Ptr, Off); +} + static bool checkConstructor(InterpState &S, CodePtr OpPC, const Function *Func, const Pointer &ThisPtr) { assert(Func->isConstructor()); @@ -1595,6 +1642,41 @@ bool CheckBitCast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits, return false; } +bool GetTypeid(InterpState &S, CodePtr OpPC, const Type *TypePtr, + const Type *TypeInfoType) { + S.Stk.push<Pointer>(TypePtr, TypeInfoType); + return true; +} + +bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType) { + const auto &P = S.Stk.pop<Pointer>(); + + if (!P.isBlockPointer()) + return false; + + if (P.isDummy()) { + QualType StarThisType = + S.getASTContext().getLValueReferenceType(P.getType()); + S.FFDiag(S.Current->getSource(OpPC), + diag::note_constexpr_polymorphic_unknown_dynamic_type) + << AK_TypeId + << P.toAPValue(S.getASTContext()) + .getAsString(S.getASTContext(), StarThisType); + return false; + } + + S.Stk.push<Pointer>(P.getType().getTypePtr(), TypeInfoType); + return true; +} + +bool DiagTypeid(InterpState &S, CodePtr OpPC) { + const auto *E = cast<CXXTypeidExpr>(S.Current->getExpr(OpPC)); + S.CCEDiag(E, diag::note_constexpr_typeid_polymorphic) + << E->getExprOperand()->getType() + << E->getExprOperand()->getSourceRange(); + return false; +} + // https://github.com/llvm/llvm-project/issues/102513 #if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG) #pragma optimize("", off) diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 8461d1e98f9777..d2aec69072e04f 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -1526,61 +1526,8 @@ inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { /// 1) Peeks a Pointer /// 2) Pushes Pointer.atField(Off) on the stack -inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) { - const Pointer &Ptr = S.Stk.peek<Pointer>(); - - if (S.getLangOpts().CPlusPlus && S.inConstantContext() && - !CheckNull(S, OpPC, Ptr, CSK_Field)) - return false; - - if (!CheckExtern(S, OpPC, Ptr)) - return false; - if (!CheckRange(S, OpPC, Ptr, CSK_Field)) - return false; - if (!CheckArray(S, OpPC, Ptr)) - return false; - if (!CheckSubobject(S, OpPC, Ptr, CSK_Field)) - return false; - - if (Ptr.isBlockPointer() && Off > Ptr.block()->getSize()) - return false; - - if (Ptr.isIntegralPointer()) { - S.Stk.push<Pointer>(Ptr.asIntPointer().atOffset(S.getASTContext(), Off)); - return true; - } - - S.Stk.push<Pointer>(Ptr.atField(Off)); - return true; -} - -inline bool GetPtrFieldPop(InterpState &S, CodePtr OpPC, uint32_t Off) { - const Pointer &Ptr = S.Stk.pop<Pointer>(); - - if (S.getLangOpts().CPlusPlus && S.inConstantContext() && - !CheckNull(S, OpPC, Ptr, CSK_Field)) - return false; - - if (!CheckExtern(S, OpPC, Ptr)) - return false; - if (!CheckRange(S, OpPC, Ptr, CSK_Field)) - return false; - if (!CheckArray(S, OpPC, Ptr)) - return false; - if (!CheckSubobject(S, OpPC, Ptr, CSK_Field)) - return false; - - if (Ptr.isBlockPointer() && Off > Ptr.block()->getSize()) - return false; - - if (Ptr.isIntegralPointer()) { - S.Stk.push<Pointer>(Ptr.asIntPointer().atOffset(S.getASTContext(), Off)); - return true; - } - - S.Stk.push<Pointer>(Ptr.atField(Off)); - return true; -} +bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off); +bool GetPtrFieldPop(InterpState &S, CodePtr OpPC, uint32_t Off); inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) { if (S.checkingPotentialConstantExpression()) @@ -3087,6 +3034,12 @@ inline bool BitCast(InterpState &S, CodePtr OpPC) { return true; } +/// Typeid support. +bool GetTypeid(InterpState &S, CodePtr OpPC, const Type *TypePtr, + const Type *TypeInfoType); +bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType); +bool DiagTypeid(InterpState &S, CodePtr OpPC); + //===----------------------------------------------------------------------===// // Read opcode arguments //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 123c21fa43eceb..4b0c902ab29268 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -850,3 +850,7 @@ def BitCastPrim : Opcode { } def BitCast : Opcode; + +def GetTypeid : Opcode { let Args = [ArgTypePtr, ArgTypePtr]; } +def GetTypeidPtr : Opcode { let Args = [ArgTypePtr]; } +def DiagTypeid : Opcode; diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp index 54484853fcdaea..f621a64d3d446d 100644 --- a/clang/lib/AST/ByteCode/Pointer.cpp +++ b/clang/lib/AST/ByteCode/Pointer.cpp @@ -96,6 +96,8 @@ void Pointer::operator=(const Pointer &P) { PointeeStorage.Int = P.PointeeStorage.Int; } else if (P.isFunctionPointer()) { PointeeStorage.Fn = P.PointeeStorage.Fn; + } else if (P.isTypeidPointer()) { + PointeeStorage.Typeid = P.PointeeStorage.Typeid; } else { assert(false && "Unhandled storage kind"); } @@ -132,6 +134,8 @@ void Pointer::operator=(Pointer &&P) { PointeeStorage.Int = P.PointeeStorage.Int; } else if (P.isFunctionPointer()) { PointeeStorage.Fn = P.PointeeStorage.Fn; + } else if (P.isTypeidPointer()) { + PointeeStorage.Typeid = P.PointeeStorage.Typeid; } else { assert(false && "Unhandled storage kind"); } @@ -151,6 +155,14 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const { if (isFunctionPointer()) return asFunctionPointer().toAPValue(ASTCtx); + if (isTypeidPointer()) { + TypeInfoLValue TypeInfo(PointeeStorage.Typeid.TypePtr); + return APValue( + APValue::LValueBase::getTypeInfo( + TypeInfo, QualType(PointeeStorage.Typeid.TypeInfoType, 0)), + CharUnits::Zero(), APValue::NoLValuePath{}); + } + // Build the lvalue base from the block. const Descriptor *Desc = getDeclDesc(); APValue::LValueBase Base; @@ -304,6 +316,8 @@ void Pointer::print(llvm::raw_ostream &OS) const { case Storage::Fn: OS << "(Fn) { " << asFunctionPointer().getFunction() << " + " << Offset << " }"; + case Storage::Typeid: + OS << "(Typeid)"; } } @@ -450,6 +464,8 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { return true; if (A.isFunctionPointer() && B.isFunctionPointer()) return true; + if (A.isTypeidPointer() && B.isTypeidPointer()) + return true; if (A.isIntegralPointer() || B.isIntegralPointer()) return A.getSource() == B.getSource(); diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index 0d467c2abf0838..ef03c12e86c100 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -49,7 +49,12 @@ struct IntPointer { IntPointer baseCast(const ASTContext &ASTCtx, unsigned BaseOffset) const; }; -enum class Storage { Block, Int, Fn }; +struct TypeidPointer { + const Type *TypePtr; + const Type *TypeInfoType; +}; + +enum class Storage { Block, Int, Fn, Typeid }; /// A pointer to a memory block, live or dead. /// @@ -107,6 +112,11 @@ class Pointer { : Offset(Offset), StorageKind(Storage::Fn) { PointeeStorage.Fn = FunctionPointer(F); } + Pointer(const Type *TypePtr, const Type *TypeInfoType, uint64_t Offset = 0) + : Offset(Offset), StorageKind(Storage::Typeid) { + PointeeStorage.Typeid.TypePtr = TypePtr; + PointeeStorage.Typeid.TypeInfoType = TypeInfoType; + } Pointer(Block *Pointee, unsigned Base, uint64_t Offset); ~Pointer(); @@ -263,6 +273,8 @@ class Pointer { return asBlockPointer().Pointee == nullptr; if (isFunctionPointer()) return asFunctionPointer().isZero(); + if (isTypeidPointer()) + return false; assert(isIntegralPointer()); return asIntPointer().Value == 0 && Offset == 0; } @@ -284,7 +296,7 @@ class Pointer { const Descriptor *getDeclDesc() const { if (isIntegralPointer()) return asIntPointer().Desc; - if (isFunctionPointer()) + if (isFunctionPointer() || isTypeidPointer()) return nullptr; assert(isBlockPointer()); @@ -337,6 +349,9 @@ class Pointer { /// Returns the type of the innermost field. QualType getType() const { + if (isTypeidPointer()) + return QualType(PointeeStorage.Typeid.TypeInfoType, 0); + if (inPrimitiveArray() && Offset != asBlockPointer().Base) { // Unfortunately, complex and vector types are not array types in clang, // but they are for us. @@ -437,7 +452,7 @@ class Pointer { } /// Pointer points directly to a block. bool isRoot() const { - if (isZero() || isIntegralPointer()) + if (isZero() || !isBlockPointer()) return true; return (asBlockPointer().Base == asBlockPointer().Pointee->getDescriptor()->getMetadataSize() || @@ -467,6 +482,7 @@ class Pointer { bool isBlockPointer() const { return StorageKind == Storage::Block; } bool isIntegralPointer() const { return StorageKind == Storage::Int; } bool isFunctionPointer() const { return StorageKind == Storage::Fn; } + bool isTypeidPointer() const { return StorageKind == Storage::Typeid; } /// Returns the record descriptor of a class. const Record *getRecord() const { return getFieldDesc()->ElemRecord; } @@ -605,7 +621,7 @@ class Pointer { /// Checks if the index is one past end. bool isOnePastEnd() const { - if (isIntegralPointer() || isFunctionPointer()) + if (!isBlockPointer()) return false; if (!asBlockPointer().Pointee) @@ -746,6 +762,7 @@ class Pointer { BlockPointer BS; IntPointer Int; FunctionPointer Fn; + TypeidPointer Typeid; } PointeeStorage; Storage StorageKind = Storage::Int; }; diff --git a/clang/test/AST/ByteCode/cxx2a.cpp b/clang/test/AST/ByteCode/cxx2a.cpp index eaae978e011843..f6006881cee4d4 100644 --- a/clang/test/AST/ByteCode/cxx2a.cpp +++ b/clang/test/AST/ByteCode/cxx2a.cpp @@ -110,3 +110,63 @@ namespace DtorOrder { } static_assert(check_abnormal_termination()); } + +namespace std { + struct type_info; +} + +namespace TypeId { + struct A { + const std::type_info &ti = typeid(*this); + }; + struct A2 : A {}; + static_assert(&A().ti == &typeid(A)); + static_assert(&typeid((A2())) == &typeid(A2)); + extern A2 extern_a2; + static_assert(&typeid(extern_a2) == &typeid(A2)); + + constexpr A2 a2; + constexpr const A &a1 = a2; + static_assert(&typeid(a1) == &typeid(A)); + + struct B { + virtual void f(); + const std::type_info &ti1 = typeid(*this); + }; + struct B2 : B { + const std::type_info &ti2 = typeid(*this); + }; + static_assert(&B2().ti1 == &typeid(B)); + static_assert(&B2().ti2 == &typeid(B2)); + extern B2 extern_b2; + static_assert(&typeid(extern_b2) == &typeid(B2)); // both-error {{constant expression}} \ + // both-note{{typeid applied to object 'extern_b2' whose dynamic type is not constant}} + + + constexpr B2 b2; + constexpr const B &b1 = b2; + static_assert(&typeid(b1) == &typeid(B2)); + + constexpr bool side_effects() { + // Not polymorphic nor a glvalue. + bool OK = true; + (void)typeid(OK = false, A2()); // both-warning {{has no effect}} + if (!OK) return false; + + // Not polymorphic. + A2 a2; + (void)typeid(OK = false, a2); // both-warning {{has no effect}} + if (!OK) return false; + + // Not a glvalue. + (void)typeid(OK = false, B2()); // both-warning {{has no effect}} + if (!OK) return false; + + // Polymorphic glvalue: operand evaluated. + OK = false; + B2 b2; + (void)typeid(OK = true, b2); // both-warning {{will be evaluated}} + return OK; + } + static_assert(side_effects()); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits