Skip to content

Commit e86b68f

Browse files
authored
[clang][bytecode] Add support for typeid pointers (#121251)
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.
1 parent 7e749d4 commit e86b68f

File tree

8 files changed

+224
-59
lines changed

8 files changed

+224
-59
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3426,6 +3426,38 @@ bool Compiler<Emitter>::VisitBlockExpr(const BlockExpr *E) {
34263426
return this->emitGetFnPtr(Func, E);
34273427
}
34283428

3429+
template <class Emitter>
3430+
bool Compiler<Emitter>::VisitCXXTypeidExpr(const CXXTypeidExpr *E) {
3431+
const Type *TypeInfoType = E->getType().getTypePtr();
3432+
3433+
if (!E->isPotentiallyEvaluated()) {
3434+
if (DiscardResult)
3435+
return true;
3436+
3437+
if (E->isTypeOperand())
3438+
return this->emitGetTypeid(
3439+
E->getTypeOperand(Ctx.getASTContext()).getTypePtr(), TypeInfoType, E);
3440+
return this->emitGetTypeid(E->getExprOperand()->getType().getTypePtr(),
3441+
TypeInfoType, E);
3442+
}
3443+
3444+
// Otherwise, we need to evaluate the expression operand.
3445+
assert(E->getExprOperand());
3446+
assert(E->getExprOperand()->isLValue());
3447+
3448+
if (!Ctx.getLangOpts().CPlusPlus20 && !this->emitDiagTypeid(E))
3449+
return false;
3450+
3451+
if (!this->visit(E->getExprOperand()))
3452+
return false;
3453+
3454+
if (!this->emitGetTypeidPtr(TypeInfoType, E))
3455+
return false;
3456+
if (DiscardResult)
3457+
return this->emitPopPtr(E);
3458+
return true;
3459+
}
3460+
34293461
template <class Emitter>
34303462
bool Compiler<Emitter>::VisitExpressionTraitExpr(const ExpressionTraitExpr *E) {
34313463
assert(Ctx.getLangOpts().CPlusPlus);

clang/lib/AST/ByteCode/Compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
205205
bool VisitCXXNewExpr(const CXXNewExpr *E);
206206
bool VisitCXXDeleteExpr(const CXXDeleteExpr *E);
207207
bool VisitBlockExpr(const BlockExpr *E);
208+
bool VisitCXXTypeidExpr(const CXXTypeidExpr *E);
208209

209210
// Statements.
210211
bool visitCompoundStmt(const CompoundStmt *S);

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,53 @@ bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T) {
11541154
return false;
11551155
}
11561156

1157+
static bool getField(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
1158+
uint32_t Off) {
1159+
if (S.getLangOpts().CPlusPlus && S.inConstantContext() &&
1160+
!CheckNull(S, OpPC, Ptr, CSK_Field))
1161+
return false;
1162+
1163+
if (!CheckExtern(S, OpPC, Ptr))
1164+
return false;
1165+
if (!CheckRange(S, OpPC, Ptr, CSK_Field))
1166+
return false;
1167+
if (!CheckArray(S, OpPC, Ptr))
1168+
return false;
1169+
if (!CheckSubobject(S, OpPC, Ptr, CSK_Field))
1170+
return false;
1171+
1172+
if (Ptr.isIntegralPointer()) {
1173+
S.Stk.push<Pointer>(Ptr.asIntPointer().atOffset(S.getASTContext(), Off));
1174+
return true;
1175+
}
1176+
1177+
if (!Ptr.isBlockPointer()) {
1178+
// FIXME: The only time we (seem to) get here is when trying to access a
1179+
// field of a typeid pointer. In that case, we're supposed to diagnose e.g.
1180+
// `typeid(int).name`, but we currently diagnose `&typeid(int)`.
1181+
S.FFDiag(S.Current->getSource(OpPC),
1182+
diag::note_constexpr_access_unreadable_object)
1183+
<< AK_Read << Ptr.toDiagnosticString(S.getASTContext());
1184+
return false;
1185+
}
1186+
1187+
if (Off > Ptr.block()->getSize())
1188+
return false;
1189+
1190+
S.Stk.push<Pointer>(Ptr.atField(Off));
1191+
return true;
1192+
}
1193+
1194+
bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
1195+
const auto &Ptr = S.Stk.peek<Pointer>();
1196+
return getField(S, OpPC, Ptr, Off);
1197+
}
1198+
1199+
bool GetPtrFieldPop(InterpState &S, CodePtr OpPC, uint32_t Off) {
1200+
const auto &Ptr = S.Stk.pop<Pointer>();
1201+
return getField(S, OpPC, Ptr, Off);
1202+
}
1203+
11571204
static bool checkConstructor(InterpState &S, CodePtr OpPC, const Function *Func,
11581205
const Pointer &ThisPtr) {
11591206
assert(Func->isConstructor());
@@ -1595,6 +1642,41 @@ bool CheckBitCast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits,
15951642
return false;
15961643
}
15971644

1645+
bool GetTypeid(InterpState &S, CodePtr OpPC, const Type *TypePtr,
1646+
const Type *TypeInfoType) {
1647+
S.Stk.push<Pointer>(TypePtr, TypeInfoType);
1648+
return true;
1649+
}
1650+
1651+
bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType) {
1652+
const auto &P = S.Stk.pop<Pointer>();
1653+
1654+
if (!P.isBlockPointer())
1655+
return false;
1656+
1657+
if (P.isDummy()) {
1658+
QualType StarThisType =
1659+
S.getASTContext().getLValueReferenceType(P.getType());
1660+
S.FFDiag(S.Current->getSource(OpPC),
1661+
diag::note_constexpr_polymorphic_unknown_dynamic_type)
1662+
<< AK_TypeId
1663+
<< P.toAPValue(S.getASTContext())
1664+
.getAsString(S.getASTContext(), StarThisType);
1665+
return false;
1666+
}
1667+
1668+
S.Stk.push<Pointer>(P.getType().getTypePtr(), TypeInfoType);
1669+
return true;
1670+
}
1671+
1672+
bool DiagTypeid(InterpState &S, CodePtr OpPC) {
1673+
const auto *E = cast<CXXTypeidExpr>(S.Current->getExpr(OpPC));
1674+
S.CCEDiag(E, diag::note_constexpr_typeid_polymorphic)
1675+
<< E->getExprOperand()->getType()
1676+
<< E->getExprOperand()->getSourceRange();
1677+
return false;
1678+
}
1679+
15981680
// https://github.com/llvm/llvm-project/issues/102513
15991681
#if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG)
16001682
#pragma optimize("", off)

clang/lib/AST/ByteCode/Interp.h

Lines changed: 8 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,61 +1526,8 @@ inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
15261526

15271527
/// 1) Peeks a Pointer
15281528
/// 2) Pushes Pointer.atField(Off) on the stack
1529-
inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
1530-
const Pointer &Ptr = S.Stk.peek<Pointer>();
1531-
1532-
if (S.getLangOpts().CPlusPlus && S.inConstantContext() &&
1533-
!CheckNull(S, OpPC, Ptr, CSK_Field))
1534-
return false;
1535-
1536-
if (!CheckExtern(S, OpPC, Ptr))
1537-
return false;
1538-
if (!CheckRange(S, OpPC, Ptr, CSK_Field))
1539-
return false;
1540-
if (!CheckArray(S, OpPC, Ptr))
1541-
return false;
1542-
if (!CheckSubobject(S, OpPC, Ptr, CSK_Field))
1543-
return false;
1544-
1545-
if (Ptr.isBlockPointer() && Off > Ptr.block()->getSize())
1546-
return false;
1547-
1548-
if (Ptr.isIntegralPointer()) {
1549-
S.Stk.push<Pointer>(Ptr.asIntPointer().atOffset(S.getASTContext(), Off));
1550-
return true;
1551-
}
1552-
1553-
S.Stk.push<Pointer>(Ptr.atField(Off));
1554-
return true;
1555-
}
1556-
1557-
inline bool GetPtrFieldPop(InterpState &S, CodePtr OpPC, uint32_t Off) {
1558-
const Pointer &Ptr = S.Stk.pop<Pointer>();
1559-
1560-
if (S.getLangOpts().CPlusPlus && S.inConstantContext() &&
1561-
!CheckNull(S, OpPC, Ptr, CSK_Field))
1562-
return false;
1563-
1564-
if (!CheckExtern(S, OpPC, Ptr))
1565-
return false;
1566-
if (!CheckRange(S, OpPC, Ptr, CSK_Field))
1567-
return false;
1568-
if (!CheckArray(S, OpPC, Ptr))
1569-
return false;
1570-
if (!CheckSubobject(S, OpPC, Ptr, CSK_Field))
1571-
return false;
1572-
1573-
if (Ptr.isBlockPointer() && Off > Ptr.block()->getSize())
1574-
return false;
1575-
1576-
if (Ptr.isIntegralPointer()) {
1577-
S.Stk.push<Pointer>(Ptr.asIntPointer().atOffset(S.getASTContext(), Off));
1578-
return true;
1579-
}
1580-
1581-
S.Stk.push<Pointer>(Ptr.atField(Off));
1582-
return true;
1583-
}
1529+
bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off);
1530+
bool GetPtrFieldPop(InterpState &S, CodePtr OpPC, uint32_t Off);
15841531

15851532
inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
15861533
if (S.checkingPotentialConstantExpression())
@@ -3087,6 +3034,12 @@ inline bool BitCast(InterpState &S, CodePtr OpPC) {
30873034
return true;
30883035
}
30893036

3037+
/// Typeid support.
3038+
bool GetTypeid(InterpState &S, CodePtr OpPC, const Type *TypePtr,
3039+
const Type *TypeInfoType);
3040+
bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType);
3041+
bool DiagTypeid(InterpState &S, CodePtr OpPC);
3042+
30903043
//===----------------------------------------------------------------------===//
30913044
// Read opcode arguments
30923045
//===----------------------------------------------------------------------===//

clang/lib/AST/ByteCode/Opcodes.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,3 +850,7 @@ def BitCastPrim : Opcode {
850850
}
851851

852852
def BitCast : Opcode;
853+
854+
def GetTypeid : Opcode { let Args = [ArgTypePtr, ArgTypePtr]; }
855+
def GetTypeidPtr : Opcode { let Args = [ArgTypePtr]; }
856+
def DiagTypeid : Opcode;

clang/lib/AST/ByteCode/Pointer.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ void Pointer::operator=(const Pointer &P) {
9696
PointeeStorage.Int = P.PointeeStorage.Int;
9797
} else if (P.isFunctionPointer()) {
9898
PointeeStorage.Fn = P.PointeeStorage.Fn;
99+
} else if (P.isTypeidPointer()) {
100+
PointeeStorage.Typeid = P.PointeeStorage.Typeid;
99101
} else {
100102
assert(false && "Unhandled storage kind");
101103
}
@@ -132,6 +134,8 @@ void Pointer::operator=(Pointer &&P) {
132134
PointeeStorage.Int = P.PointeeStorage.Int;
133135
} else if (P.isFunctionPointer()) {
134136
PointeeStorage.Fn = P.PointeeStorage.Fn;
137+
} else if (P.isTypeidPointer()) {
138+
PointeeStorage.Typeid = P.PointeeStorage.Typeid;
135139
} else {
136140
assert(false && "Unhandled storage kind");
137141
}
@@ -151,6 +155,14 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const {
151155
if (isFunctionPointer())
152156
return asFunctionPointer().toAPValue(ASTCtx);
153157

158+
if (isTypeidPointer()) {
159+
TypeInfoLValue TypeInfo(PointeeStorage.Typeid.TypePtr);
160+
return APValue(
161+
APValue::LValueBase::getTypeInfo(
162+
TypeInfo, QualType(PointeeStorage.Typeid.TypeInfoType, 0)),
163+
CharUnits::Zero(), APValue::NoLValuePath{});
164+
}
165+
154166
// Build the lvalue base from the block.
155167
const Descriptor *Desc = getDeclDesc();
156168
APValue::LValueBase Base;
@@ -304,6 +316,8 @@ void Pointer::print(llvm::raw_ostream &OS) const {
304316
case Storage::Fn:
305317
OS << "(Fn) { " << asFunctionPointer().getFunction() << " + " << Offset
306318
<< " }";
319+
case Storage::Typeid:
320+
OS << "(Typeid)";
307321
}
308322
}
309323

@@ -450,6 +464,8 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
450464
return true;
451465
if (A.isFunctionPointer() && B.isFunctionPointer())
452466
return true;
467+
if (A.isTypeidPointer() && B.isTypeidPointer())
468+
return true;
453469

454470
if (A.isIntegralPointer() || B.isIntegralPointer())
455471
return A.getSource() == B.getSource();

clang/lib/AST/ByteCode/Pointer.h

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ struct IntPointer {
4949
IntPointer baseCast(const ASTContext &ASTCtx, unsigned BaseOffset) const;
5050
};
5151

52-
enum class Storage { Block, Int, Fn };
52+
struct TypeidPointer {
53+
const Type *TypePtr;
54+
const Type *TypeInfoType;
55+
};
56+
57+
enum class Storage { Block, Int, Fn, Typeid };
5358

5459
/// A pointer to a memory block, live or dead.
5560
///
@@ -107,6 +112,11 @@ class Pointer {
107112
: Offset(Offset), StorageKind(Storage::Fn) {
108113
PointeeStorage.Fn = FunctionPointer(F);
109114
}
115+
Pointer(const Type *TypePtr, const Type *TypeInfoType, uint64_t Offset = 0)
116+
: Offset(Offset), StorageKind(Storage::Typeid) {
117+
PointeeStorage.Typeid.TypePtr = TypePtr;
118+
PointeeStorage.Typeid.TypeInfoType = TypeInfoType;
119+
}
110120
Pointer(Block *Pointee, unsigned Base, uint64_t Offset);
111121
~Pointer();
112122

@@ -263,6 +273,8 @@ class Pointer {
263273
return asBlockPointer().Pointee == nullptr;
264274
if (isFunctionPointer())
265275
return asFunctionPointer().isZero();
276+
if (isTypeidPointer())
277+
return false;
266278
assert(isIntegralPointer());
267279
return asIntPointer().Value == 0 && Offset == 0;
268280
}
@@ -284,7 +296,7 @@ class Pointer {
284296
const Descriptor *getDeclDesc() const {
285297
if (isIntegralPointer())
286298
return asIntPointer().Desc;
287-
if (isFunctionPointer())
299+
if (isFunctionPointer() || isTypeidPointer())
288300
return nullptr;
289301

290302
assert(isBlockPointer());
@@ -337,6 +349,9 @@ class Pointer {
337349

338350
/// Returns the type of the innermost field.
339351
QualType getType() const {
352+
if (isTypeidPointer())
353+
return QualType(PointeeStorage.Typeid.TypeInfoType, 0);
354+
340355
if (inPrimitiveArray() && Offset != asBlockPointer().Base) {
341356
// Unfortunately, complex and vector types are not array types in clang,
342357
// but they are for us.
@@ -437,7 +452,7 @@ class Pointer {
437452
}
438453
/// Pointer points directly to a block.
439454
bool isRoot() const {
440-
if (isZero() || isIntegralPointer())
455+
if (isZero() || !isBlockPointer())
441456
return true;
442457
return (asBlockPointer().Base ==
443458
asBlockPointer().Pointee->getDescriptor()->getMetadataSize() ||
@@ -467,6 +482,7 @@ class Pointer {
467482
bool isBlockPointer() const { return StorageKind == Storage::Block; }
468483
bool isIntegralPointer() const { return StorageKind == Storage::Int; }
469484
bool isFunctionPointer() const { return StorageKind == Storage::Fn; }
485+
bool isTypeidPointer() const { return StorageKind == Storage::Typeid; }
470486

471487
/// Returns the record descriptor of a class.
472488
const Record *getRecord() const { return getFieldDesc()->ElemRecord; }
@@ -605,7 +621,7 @@ class Pointer {
605621

606622
/// Checks if the index is one past end.
607623
bool isOnePastEnd() const {
608-
if (isIntegralPointer() || isFunctionPointer())
624+
if (!isBlockPointer())
609625
return false;
610626

611627
if (!asBlockPointer().Pointee)
@@ -746,6 +762,7 @@ class Pointer {
746762
BlockPointer BS;
747763
IntPointer Int;
748764
FunctionPointer Fn;
765+
TypeidPointer Typeid;
749766
} PointeeStorage;
750767
Storage StorageKind = Storage::Int;
751768
};

0 commit comments

Comments
 (0)