[clang] [clang][bytecode] Add support for typeid pointers (PR #121251)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Fri Dec 27 22:38:54 PST 2024
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?= <tbaeder at 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());
+}
More information about the cfe-commits
mailing list