Skip to content

Commit

Permalink
Refactor constant evaluation of typeid(T) to track a symbolic type_info
Browse files Browse the repository at this point in the history
object rather than tracking the originating expression.

This is groundwork for supporting polymorphic typeid expressions. (Note
that this somewhat regresses our support for DR1968, but it turns out
that that never actually worked anyway, at least in non-trivial cases.)

This reinstates r360974, reverted in r360988, with a fix for a
static_assert failure on 32-bit builds: force Type base class to have
8-byte alignment like the rest of Clang's AST nodes.

llvm-svn: 360995
  • Loading branch information
zygoloid committed May 17, 2019
1 parent 5652063 commit ee0ce30
Show file tree
Hide file tree
Showing 12 changed files with 201 additions and 58 deletions.
92 changes: 61 additions & 31 deletions clang/include/clang/AST/APValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,52 @@ namespace clang {
class AddrLabelExpr;
class ASTContext;
class CharUnits;
class CXXRecordDecl;
class Decl;
class DiagnosticBuilder;
class Expr;
class FieldDecl;
class Decl;
struct PrintingPolicy;
class Type;
class ValueDecl;
class CXXRecordDecl;
class QualType;

/// Symbolic representation of typeid(T) for some type T.
class TypeInfoLValue {
const Type *T;

public:
TypeInfoLValue() : T() {}
explicit TypeInfoLValue(const Type *T);

const Type *getType() const { return T; }
explicit operator bool() const { return T; }

void *getOpaqueValue() { return const_cast<Type*>(T); }
static TypeInfoLValue getFromOpaqueValue(void *Value) {
TypeInfoLValue V;
V.T = reinterpret_cast<const Type*>(Value);
return V;
}

void print(llvm::raw_ostream &Out, const PrintingPolicy &Policy) const;
};
}

namespace llvm {
template<> struct PointerLikeTypeTraits<clang::TypeInfoLValue> {
static void *getAsVoidPointer(clang::TypeInfoLValue V) {
return V.getOpaqueValue();
}
static clang::TypeInfoLValue getFromVoidPointer(void *P) {
return clang::TypeInfoLValue::getFromOpaqueValue(P);
}
// Validated by static_assert in APValue.cpp; hardcoded to avoid needing
// to include Type.h.
static constexpr int NumLowBitsAvailable = 3;
};
}

namespace clang {
/// APValue - This class implements a discriminated union of [uninitialized]
/// [APSInt] [APFloat], [Complex APSInt] [Complex APFloat], [Expr + Offset],
/// [Vector: N * APValue], [Array: N * APValue]
Expand All @@ -56,14 +94,14 @@ class APValue {
};

class LValueBase {
public:
typedef llvm::PointerUnion<const ValueDecl *, const Expr *> PtrTy;

LValueBase() : CallIndex(0), Version(0) {}
typedef llvm::PointerUnion<const ValueDecl *, const Expr *, TypeInfoLValue>
PtrTy;

template <class T>
LValueBase(T P, unsigned I = 0, unsigned V = 0)
: Ptr(P), CallIndex(I), Version(V) {}
public:
LValueBase() : Local{} {}
LValueBase(const ValueDecl *P, unsigned I = 0, unsigned V = 0);
LValueBase(const Expr *P, unsigned I = 0, unsigned V = 0);
static LValueBase getTypeInfo(TypeInfoLValue LV, QualType TypeInfo);

template <class T>
bool is() const { return Ptr.is<T>(); }
Expand All @@ -78,36 +116,28 @@ class APValue {

bool isNull() const;

explicit operator bool () const;
explicit operator bool() const;

PtrTy getPointer() const {
return Ptr;
}

unsigned getCallIndex() const {
return CallIndex;
}

void setCallIndex(unsigned Index) {
CallIndex = Index;
}

unsigned getVersion() const {
return Version;
}
unsigned getCallIndex() const;
unsigned getVersion() const;
QualType getTypeInfoType() const;

friend bool operator==(const LValueBase &LHS, const LValueBase &RHS) {
return LHS.Ptr == RHS.Ptr && LHS.CallIndex == RHS.CallIndex &&
LHS.Version == RHS.Version;
}
friend bool operator==(const LValueBase &LHS, const LValueBase &RHS);
friend bool operator!=(const LValueBase &LHS, const LValueBase &RHS) {
return !(LHS == RHS);
}
friend llvm::hash_code hash_value(const LValueBase &Base);

private:
PtrTy Ptr;
unsigned CallIndex, Version;
struct LocalState {
unsigned CallIndex, Version;
};
union {
LocalState Local;
/// The type std::type_info, if this is a TypeInfoLValue.
void *TypeInfoType;
};
};

/// A FieldDecl or CXXRecordDecl, along with a flag indicating whether we
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1411,7 +1411,7 @@ enum class AutoTypeKeyword {
///
/// Types, once created, are immutable.
///
class Type : public ExtQualsTypeCommonBase {
class alignas(8) Type : public ExtQualsTypeCommonBase {
public:
enum TypeClass {
#define TYPE(Class, Base) Class,
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticASTKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ def note_constexpr_access_static_temporary : Note<
"dynamic_cast of}0 temporary "
"is not allowed in a constant expression outside the expression that "
"created the temporary">;
def note_constexpr_access_unreadable_object : Note<
"%select{read of|assignment to|increment of|decrement of|member call on|"
"dynamic_cast of}0 object '%1' whose value is not known">;
def note_constexpr_modify_global : Note<
"a constant expression cannot modify an object that is visible outside "
"that expression">;
Expand Down
72 changes: 65 additions & 7 deletions clang/lib/AST/APValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,61 @@
#include "llvm/Support/raw_ostream.h"
using namespace clang;

/// The identity of a type_info object depends on the canonical unqualified
/// type only.
TypeInfoLValue::TypeInfoLValue(const Type *T)
: T(T->getCanonicalTypeUnqualified().getTypePtr()) {}

void TypeInfoLValue::print(llvm::raw_ostream &Out,
const PrintingPolicy &Policy) const {
Out << "typeid(";
QualType(getType(), 0).print(Out, Policy);
Out << ")";
}

static_assert(
1 << llvm::PointerLikeTypeTraits<TypeInfoLValue>::NumLowBitsAvailable <=
alignof(const Type *),
"Type is insufficiently aligned");

APValue::LValueBase::LValueBase(const ValueDecl *P, unsigned I, unsigned V)
: Ptr(P), Local{I, V} {}
APValue::LValueBase::LValueBase(const Expr *P, unsigned I, unsigned V)
: Ptr(P), Local{I, V} {}

APValue::LValueBase APValue::LValueBase::getTypeInfo(TypeInfoLValue LV,
QualType TypeInfo) {
LValueBase Base;
Base.Ptr = LV;
Base.TypeInfoType = TypeInfo.getAsOpaquePtr();
return Base;
}

unsigned APValue::LValueBase::getCallIndex() const {
return is<TypeInfoLValue>() ? 0 : Local.CallIndex;
}

unsigned APValue::LValueBase::getVersion() const {
return is<TypeInfoLValue>() ? 0 : Local.Version;
}

QualType APValue::LValueBase::getTypeInfoType() const {
assert(is<TypeInfoLValue>() && "not a type_info lvalue");
return QualType::getFromOpaquePtr(TypeInfoType);
}

namespace clang {
bool operator==(const APValue::LValueBase &LHS,
const APValue::LValueBase &RHS) {
if (LHS.Ptr != RHS.Ptr)
return false;
if (LHS.is<TypeInfoLValue>())
return true;
return LHS.Local.CallIndex == RHS.Local.CallIndex &&
LHS.Local.Version == RHS.Local.Version;
}
}

namespace {
struct LVBase {
APValue::LValueBase Base;
Expand All @@ -45,21 +100,19 @@ APValue::LValueBase::operator bool () const {
clang::APValue::LValueBase
llvm::DenseMapInfo<clang::APValue::LValueBase>::getEmptyKey() {
return clang::APValue::LValueBase(
DenseMapInfo<clang::APValue::LValueBase::PtrTy>::getEmptyKey(),
DenseMapInfo<unsigned>::getEmptyKey(),
DenseMapInfo<unsigned>::getEmptyKey());
DenseMapInfo<const ValueDecl*>::getEmptyKey());
}

clang::APValue::LValueBase
llvm::DenseMapInfo<clang::APValue::LValueBase>::getTombstoneKey() {
return clang::APValue::LValueBase(
DenseMapInfo<clang::APValue::LValueBase::PtrTy>::getTombstoneKey(),
DenseMapInfo<unsigned>::getTombstoneKey(),
DenseMapInfo<unsigned>::getTombstoneKey());
DenseMapInfo<const ValueDecl*>::getTombstoneKey());
}

namespace clang {
llvm::hash_code hash_value(const APValue::LValueBase &Base) {
if (Base.is<TypeInfoLValue>())
return llvm::hash_value(Base.getOpaqueValue());
return llvm::hash_combine(Base.getOpaqueValue(), Base.getCallIndex(),
Base.getVersion());
}
Expand Down Expand Up @@ -470,7 +523,9 @@ void APValue::printPretty(raw_ostream &Out, ASTContext &Ctx, QualType Ty) const{

if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>())
Out << *VD;
else {
else if (TypeInfoLValue TI = Base.dyn_cast<TypeInfoLValue>()) {
TI.print(Out, Ctx.getPrintingPolicy());
} else {
assert(Base.get<const Expr *>() != nullptr &&
"Expecting non-null Expr");
Base.get<const Expr*>()->printPretty(Out, nullptr,
Expand All @@ -495,6 +550,9 @@ void APValue::printPretty(raw_ostream &Out, ASTContext &Ctx, QualType Ty) const{
if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>()) {
Out << *VD;
ElemTy = VD->getType();
} else if (TypeInfoLValue TI = Base.dyn_cast<TypeInfoLValue>()) {
TI.print(Out, Ctx.getPrintingPolicy());
ElemTy = Base.getTypeInfoType();
} else {
const Expr *E = Base.get<const Expr*>();
assert(E != nullptr && "Expecting non-null Expr");
Expand Down
47 changes: 36 additions & 11 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ namespace {
return D->getType();
}

if (TypeInfoLValue TI = B.dyn_cast<TypeInfoLValue>())
return B.getTypeInfoType();

const Expr *Base = B.get<const Expr*>();

// For a materialized temporary, the type of the temporary we materialized
Expand Down Expand Up @@ -1783,6 +1786,9 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
return isa<FunctionDecl>(D);
}

if (B.is<TypeInfoLValue>())
return true;

const Expr *E = B.get<const Expr*>();
switch (E->getStmtClass()) {
default:
Expand All @@ -1800,7 +1806,6 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
case Expr::PredefinedExprClass:
case Expr::ObjCStringLiteralClass:
case Expr::ObjCEncodeExprClass:
case Expr::CXXTypeidExprClass:
case Expr::CXXUuidofExprClass:
return true;
case Expr::ObjCBoxedExprClass:
Expand Down Expand Up @@ -1878,9 +1883,9 @@ static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) {
const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>();
if (VD)
Info.Note(VD->getLocation(), diag::note_declared_at);
else
Info.Note(Base.get<const Expr*>()->getExprLoc(),
diag::note_constexpr_temporary_here);
else if (const Expr *E = Base.dyn_cast<const Expr*>())
Info.Note(E->getExprLoc(), diag::note_constexpr_temporary_here);
// We have no information to show for a typeid(T) object.
}

/// Check that this reference or pointer core constant expression is a valid
Expand Down Expand Up @@ -3404,7 +3409,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,

if (!Frame) {
if (const MaterializeTemporaryExpr *MTE =
dyn_cast<MaterializeTemporaryExpr>(Base)) {
dyn_cast_or_null<MaterializeTemporaryExpr>(Base)) {
assert(MTE->getStorageDuration() == SD_Static &&
"should have a frame for a non-global materialized temporary");

Expand Down Expand Up @@ -3439,7 +3444,13 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
} else {
if (!IsAccess)
return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);
Info.FFDiag(E);
APValue Val;
LVal.moveInto(Val);
Info.FFDiag(E, diag::note_constexpr_access_unreadable_object)
<< AK
<< Val.getAsString(Info.Ctx,
Info.Ctx.getLValueReferenceType(LValType));
NoteLValueLocation(Info, LVal.Base);
return CompleteObject();
}
} else {
Expand Down Expand Up @@ -5777,13 +5788,13 @@ class LValueExprEvaluatorBase
// - Literals
// * CompoundLiteralExpr in C (and in global scope in C++)
// * StringLiteral
// * CXXTypeidExpr
// * PredefinedExpr
// * ObjCStringLiteralExpr
// * ObjCEncodeExpr
// * AddrLabelExpr
// * BlockExpr
// * CallExpr for a MakeStringConstant builtin
// - typeid(T) expressions, as TypeInfoLValues
// - Locals and temporaries
// * MaterializeTemporaryExpr
// * Any Expr, with a CallIndex indicating the function in which the temporary
Expand Down Expand Up @@ -6018,8 +6029,14 @@ LValueExprEvaluator::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
}

bool LValueExprEvaluator::VisitCXXTypeidExpr(const CXXTypeidExpr *E) {
if (!E->isPotentiallyEvaluated())
return Success(E);
if (!E->isPotentiallyEvaluated()) {
TypeInfoLValue TypeInfo;
if (E->isTypeOperand())
TypeInfo = TypeInfoLValue(E->getTypeOperand(Info.Ctx).getTypePtr());
else
TypeInfo = TypeInfoLValue(E->getExprOperand()->getType().getTypePtr());
return Success(APValue::LValueBase::getTypeInfo(TypeInfo, E->getType()));
}

Info.FFDiag(E, diag::note_constexpr_typeid_polymorphic)
<< E->getExprOperand()->getType()
Expand Down Expand Up @@ -6615,9 +6632,11 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
if (const ValueDecl *VD =
OffsetResult.Base.dyn_cast<const ValueDecl*>()) {
BaseAlignment = Info.Ctx.getDeclAlign(VD);
} else if (const Expr *E = OffsetResult.Base.dyn_cast<const Expr *>()) {
BaseAlignment = GetAlignOfExpr(Info, E, UETT_AlignOf);
} else {
BaseAlignment = GetAlignOfExpr(
Info, OffsetResult.Base.get<const Expr *>(), UETT_AlignOf);
BaseAlignment = GetAlignOfType(
Info, OffsetResult.Base.getTypeInfoType(), UETT_AlignOf);
}

if (BaseAlignment < Align) {
Expand Down Expand Up @@ -8335,6 +8354,10 @@ static bool EvaluateBuiltinConstantPForLValue(const APValue &LV) {
if (!isa<StringLiteral>(E))
return false;
return LV.getLValueOffset().isZero();
} else if (Base.is<TypeInfoLValue>()) {
// Surprisingly, GCC considers __builtin_constant_p(&typeid(int)) to
// evaluate to true.
return true;
} else {
// Any other base is not constant enough for GCC.
return false;
Expand Down Expand Up @@ -8399,6 +8422,8 @@ static QualType getObjectType(APValue::LValueBase B) {
} else if (const Expr *E = B.get<const Expr*>()) {
if (isa<CompoundLiteralExpr>(E))
return E->getType();
} else if (B.is<TypeInfoLValue>()) {
return B.getTypeInfoType();
}

return QualType();
Expand Down
Loading

0 comments on commit ee0ce30

Please sign in to comment.