-
Notifications
You must be signed in to change notification settings - Fork 15k
[clang][bytecode] Add support for typeid pointers #121251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang Author: Timm Baeder (tbaederr) ChangesAdd it as another kind of pointer, saving both a Full diff: https://github.com/llvm/llvm-project/pull/121251.diff 8 Files Affected:
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..d6e58d9f1190b3 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;
@@ -450,6 +462,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());
+}
|
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.
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/145/builds/4101 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/64/builds/1803 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/186/builds/5214 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/72/builds/6693 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/51/builds/8284 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/66/builds/8030 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/168/builds/6991 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/195/builds/2861 Here is the relevant piece of the build log for the reference
|
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.