Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ByteCode/Compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
82 changes: 82 additions & 0 deletions clang/lib/AST/ByteCode/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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)
Expand Down
63 changes: 8 additions & 55 deletions clang/lib/AST/ByteCode/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/ByteCode/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -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;
16 changes: 16 additions & 0 deletions clang/lib/AST/ByteCode/Pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down Expand Up @@ -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");
}
Expand All @@ -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;
Expand Down Expand Up @@ -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)";
}
}

Expand Down Expand Up @@ -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();
Expand Down
25 changes: 21 additions & 4 deletions clang/lib/AST/ByteCode/Pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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;
}
Expand All @@ -284,7 +296,7 @@ class Pointer {
const Descriptor *getDeclDesc() const {
if (isIntegralPointer())
return asIntPointer().Desc;
if (isFunctionPointer())
if (isFunctionPointer() || isTypeidPointer())
return nullptr;

assert(isBlockPointer());
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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() ||
Expand Down Expand Up @@ -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; }
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -746,6 +762,7 @@ class Pointer {
BlockPointer BS;
IntPointer Int;
FunctionPointer Fn;
TypeidPointer Typeid;
} PointeeStorage;
Storage StorageKind = Storage::Int;
};
Expand Down
Loading
Loading