Skip to content

Commit ee0ce30

Browse files
committed
Refactor constant evaluation of typeid(T) to track a symbolic type_info
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
1 parent 5652063 commit ee0ce30

File tree

12 files changed

+201
-58
lines changed

12 files changed

+201
-58
lines changed

clang/include/clang/AST/APValue.h

+61-31
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,52 @@ namespace clang {
2424
class AddrLabelExpr;
2525
class ASTContext;
2626
class CharUnits;
27+
class CXXRecordDecl;
28+
class Decl;
2729
class DiagnosticBuilder;
2830
class Expr;
2931
class FieldDecl;
30-
class Decl;
32+
struct PrintingPolicy;
33+
class Type;
3134
class ValueDecl;
32-
class CXXRecordDecl;
33-
class QualType;
3435

36+
/// Symbolic representation of typeid(T) for some type T.
37+
class TypeInfoLValue {
38+
const Type *T;
39+
40+
public:
41+
TypeInfoLValue() : T() {}
42+
explicit TypeInfoLValue(const Type *T);
43+
44+
const Type *getType() const { return T; }
45+
explicit operator bool() const { return T; }
46+
47+
void *getOpaqueValue() { return const_cast<Type*>(T); }
48+
static TypeInfoLValue getFromOpaqueValue(void *Value) {
49+
TypeInfoLValue V;
50+
V.T = reinterpret_cast<const Type*>(Value);
51+
return V;
52+
}
53+
54+
void print(llvm::raw_ostream &Out, const PrintingPolicy &Policy) const;
55+
};
56+
}
57+
58+
namespace llvm {
59+
template<> struct PointerLikeTypeTraits<clang::TypeInfoLValue> {
60+
static void *getAsVoidPointer(clang::TypeInfoLValue V) {
61+
return V.getOpaqueValue();
62+
}
63+
static clang::TypeInfoLValue getFromVoidPointer(void *P) {
64+
return clang::TypeInfoLValue::getFromOpaqueValue(P);
65+
}
66+
// Validated by static_assert in APValue.cpp; hardcoded to avoid needing
67+
// to include Type.h.
68+
static constexpr int NumLowBitsAvailable = 3;
69+
};
70+
}
71+
72+
namespace clang {
3573
/// APValue - This class implements a discriminated union of [uninitialized]
3674
/// [APSInt] [APFloat], [Complex APSInt] [Complex APFloat], [Expr + Offset],
3775
/// [Vector: N * APValue], [Array: N * APValue]
@@ -56,14 +94,14 @@ class APValue {
5694
};
5795

5896
class LValueBase {
59-
public:
60-
typedef llvm::PointerUnion<const ValueDecl *, const Expr *> PtrTy;
61-
62-
LValueBase() : CallIndex(0), Version(0) {}
97+
typedef llvm::PointerUnion<const ValueDecl *, const Expr *, TypeInfoLValue>
98+
PtrTy;
6399

64-
template <class T>
65-
LValueBase(T P, unsigned I = 0, unsigned V = 0)
66-
: Ptr(P), CallIndex(I), Version(V) {}
100+
public:
101+
LValueBase() : Local{} {}
102+
LValueBase(const ValueDecl *P, unsigned I = 0, unsigned V = 0);
103+
LValueBase(const Expr *P, unsigned I = 0, unsigned V = 0);
104+
static LValueBase getTypeInfo(TypeInfoLValue LV, QualType TypeInfo);
67105

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

79117
bool isNull() const;
80118

81-
explicit operator bool () const;
119+
explicit operator bool() const;
82120

83-
PtrTy getPointer() const {
84-
return Ptr;
85-
}
86-
87-
unsigned getCallIndex() const {
88-
return CallIndex;
89-
}
90-
91-
void setCallIndex(unsigned Index) {
92-
CallIndex = Index;
93-
}
94-
95-
unsigned getVersion() const {
96-
return Version;
97-
}
121+
unsigned getCallIndex() const;
122+
unsigned getVersion() const;
123+
QualType getTypeInfoType() const;
98124

99-
friend bool operator==(const LValueBase &LHS, const LValueBase &RHS) {
100-
return LHS.Ptr == RHS.Ptr && LHS.CallIndex == RHS.CallIndex &&
101-
LHS.Version == RHS.Version;
102-
}
125+
friend bool operator==(const LValueBase &LHS, const LValueBase &RHS);
103126
friend bool operator!=(const LValueBase &LHS, const LValueBase &RHS) {
104127
return !(LHS == RHS);
105128
}
106129
friend llvm::hash_code hash_value(const LValueBase &Base);
107130

108131
private:
109132
PtrTy Ptr;
110-
unsigned CallIndex, Version;
133+
struct LocalState {
134+
unsigned CallIndex, Version;
135+
};
136+
union {
137+
LocalState Local;
138+
/// The type std::type_info, if this is a TypeInfoLValue.
139+
void *TypeInfoType;
140+
};
111141
};
112142

113143
/// A FieldDecl or CXXRecordDecl, along with a flag indicating whether we

clang/include/clang/AST/Type.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1411,7 +1411,7 @@ enum class AutoTypeKeyword {
14111411
///
14121412
/// Types, once created, are immutable.
14131413
///
1414-
class Type : public ExtQualsTypeCommonBase {
1414+
class alignas(8) Type : public ExtQualsTypeCommonBase {
14151415
public:
14161416
enum TypeClass {
14171417
#define TYPE(Class, Base) Class,

clang/include/clang/Basic/DiagnosticASTKinds.td

+3
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ def note_constexpr_access_static_temporary : Note<
160160
"dynamic_cast of}0 temporary "
161161
"is not allowed in a constant expression outside the expression that "
162162
"created the temporary">;
163+
def note_constexpr_access_unreadable_object : Note<
164+
"%select{read of|assignment to|increment of|decrement of|member call on|"
165+
"dynamic_cast of}0 object '%1' whose value is not known">;
163166
def note_constexpr_modify_global : Note<
164167
"a constant expression cannot modify an object that is visible outside "
165168
"that expression">;

clang/lib/AST/APValue.cpp

+65-7
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,61 @@
2020
#include "llvm/Support/raw_ostream.h"
2121
using namespace clang;
2222

23+
/// The identity of a type_info object depends on the canonical unqualified
24+
/// type only.
25+
TypeInfoLValue::TypeInfoLValue(const Type *T)
26+
: T(T->getCanonicalTypeUnqualified().getTypePtr()) {}
27+
28+
void TypeInfoLValue::print(llvm::raw_ostream &Out,
29+
const PrintingPolicy &Policy) const {
30+
Out << "typeid(";
31+
QualType(getType(), 0).print(Out, Policy);
32+
Out << ")";
33+
}
34+
35+
static_assert(
36+
1 << llvm::PointerLikeTypeTraits<TypeInfoLValue>::NumLowBitsAvailable <=
37+
alignof(const Type *),
38+
"Type is insufficiently aligned");
39+
40+
APValue::LValueBase::LValueBase(const ValueDecl *P, unsigned I, unsigned V)
41+
: Ptr(P), Local{I, V} {}
42+
APValue::LValueBase::LValueBase(const Expr *P, unsigned I, unsigned V)
43+
: Ptr(P), Local{I, V} {}
44+
45+
APValue::LValueBase APValue::LValueBase::getTypeInfo(TypeInfoLValue LV,
46+
QualType TypeInfo) {
47+
LValueBase Base;
48+
Base.Ptr = LV;
49+
Base.TypeInfoType = TypeInfo.getAsOpaquePtr();
50+
return Base;
51+
}
52+
53+
unsigned APValue::LValueBase::getCallIndex() const {
54+
return is<TypeInfoLValue>() ? 0 : Local.CallIndex;
55+
}
56+
57+
unsigned APValue::LValueBase::getVersion() const {
58+
return is<TypeInfoLValue>() ? 0 : Local.Version;
59+
}
60+
61+
QualType APValue::LValueBase::getTypeInfoType() const {
62+
assert(is<TypeInfoLValue>() && "not a type_info lvalue");
63+
return QualType::getFromOpaquePtr(TypeInfoType);
64+
}
65+
66+
namespace clang {
67+
bool operator==(const APValue::LValueBase &LHS,
68+
const APValue::LValueBase &RHS) {
69+
if (LHS.Ptr != RHS.Ptr)
70+
return false;
71+
if (LHS.is<TypeInfoLValue>())
72+
return true;
73+
return LHS.Local.CallIndex == RHS.Local.CallIndex &&
74+
LHS.Local.Version == RHS.Local.Version;
75+
}
76+
}
77+
2378
namespace {
2479
struct LVBase {
2580
APValue::LValueBase Base;
@@ -45,21 +100,19 @@ APValue::LValueBase::operator bool () const {
45100
clang::APValue::LValueBase
46101
llvm::DenseMapInfo<clang::APValue::LValueBase>::getEmptyKey() {
47102
return clang::APValue::LValueBase(
48-
DenseMapInfo<clang::APValue::LValueBase::PtrTy>::getEmptyKey(),
49-
DenseMapInfo<unsigned>::getEmptyKey(),
50-
DenseMapInfo<unsigned>::getEmptyKey());
103+
DenseMapInfo<const ValueDecl*>::getEmptyKey());
51104
}
52105

53106
clang::APValue::LValueBase
54107
llvm::DenseMapInfo<clang::APValue::LValueBase>::getTombstoneKey() {
55108
return clang::APValue::LValueBase(
56-
DenseMapInfo<clang::APValue::LValueBase::PtrTy>::getTombstoneKey(),
57-
DenseMapInfo<unsigned>::getTombstoneKey(),
58-
DenseMapInfo<unsigned>::getTombstoneKey());
109+
DenseMapInfo<const ValueDecl*>::getTombstoneKey());
59110
}
60111

61112
namespace clang {
62113
llvm::hash_code hash_value(const APValue::LValueBase &Base) {
114+
if (Base.is<TypeInfoLValue>())
115+
return llvm::hash_value(Base.getOpaqueValue());
63116
return llvm::hash_combine(Base.getOpaqueValue(), Base.getCallIndex(),
64117
Base.getVersion());
65118
}
@@ -470,7 +523,9 @@ void APValue::printPretty(raw_ostream &Out, ASTContext &Ctx, QualType Ty) const{
470523

471524
if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>())
472525
Out << *VD;
473-
else {
526+
else if (TypeInfoLValue TI = Base.dyn_cast<TypeInfoLValue>()) {
527+
TI.print(Out, Ctx.getPrintingPolicy());
528+
} else {
474529
assert(Base.get<const Expr *>() != nullptr &&
475530
"Expecting non-null Expr");
476531
Base.get<const Expr*>()->printPretty(Out, nullptr,
@@ -495,6 +550,9 @@ void APValue::printPretty(raw_ostream &Out, ASTContext &Ctx, QualType Ty) const{
495550
if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>()) {
496551
Out << *VD;
497552
ElemTy = VD->getType();
553+
} else if (TypeInfoLValue TI = Base.dyn_cast<TypeInfoLValue>()) {
554+
TI.print(Out, Ctx.getPrintingPolicy());
555+
ElemTy = Base.getTypeInfoType();
498556
} else {
499557
const Expr *E = Base.get<const Expr*>();
500558
assert(E != nullptr && "Expecting non-null Expr");

clang/lib/AST/ExprConstant.cpp

+36-11
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ namespace {
8787
return D->getType();
8888
}
8989

90+
if (TypeInfoLValue TI = B.dyn_cast<TypeInfoLValue>())
91+
return B.getTypeInfoType();
92+
9093
const Expr *Base = B.get<const Expr*>();
9194

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

1789+
if (B.is<TypeInfoLValue>())
1790+
return true;
1791+
17861792
const Expr *E = B.get<const Expr*>();
17871793
switch (E->getStmtClass()) {
17881794
default:
@@ -1800,7 +1806,6 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
18001806
case Expr::PredefinedExprClass:
18011807
case Expr::ObjCStringLiteralClass:
18021808
case Expr::ObjCEncodeExprClass:
1803-
case Expr::CXXTypeidExprClass:
18041809
case Expr::CXXUuidofExprClass:
18051810
return true;
18061811
case Expr::ObjCBoxedExprClass:
@@ -1878,9 +1883,9 @@ static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) {
18781883
const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>();
18791884
if (VD)
18801885
Info.Note(VD->getLocation(), diag::note_declared_at);
1881-
else
1882-
Info.Note(Base.get<const Expr*>()->getExprLoc(),
1883-
diag::note_constexpr_temporary_here);
1886+
else if (const Expr *E = Base.dyn_cast<const Expr*>())
1887+
Info.Note(E->getExprLoc(), diag::note_constexpr_temporary_here);
1888+
// We have no information to show for a typeid(T) object.
18841889
}
18851890

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

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

@@ -3439,7 +3444,13 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
34393444
} else {
34403445
if (!IsAccess)
34413446
return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);
3442-
Info.FFDiag(E);
3447+
APValue Val;
3448+
LVal.moveInto(Val);
3449+
Info.FFDiag(E, diag::note_constexpr_access_unreadable_object)
3450+
<< AK
3451+
<< Val.getAsString(Info.Ctx,
3452+
Info.Ctx.getLValueReferenceType(LValType));
3453+
NoteLValueLocation(Info, LVal.Base);
34433454
return CompleteObject();
34443455
}
34453456
} else {
@@ -5777,13 +5788,13 @@ class LValueExprEvaluatorBase
57775788
// - Literals
57785789
// * CompoundLiteralExpr in C (and in global scope in C++)
57795790
// * StringLiteral
5780-
// * CXXTypeidExpr
57815791
// * PredefinedExpr
57825792
// * ObjCStringLiteralExpr
57835793
// * ObjCEncodeExpr
57845794
// * AddrLabelExpr
57855795
// * BlockExpr
57865796
// * CallExpr for a MakeStringConstant builtin
5797+
// - typeid(T) expressions, as TypeInfoLValues
57875798
// - Locals and temporaries
57885799
// * MaterializeTemporaryExpr
57895800
// * Any Expr, with a CallIndex indicating the function in which the temporary
@@ -6018,8 +6029,14 @@ LValueExprEvaluator::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
60186029
}
60196030

60206031
bool LValueExprEvaluator::VisitCXXTypeidExpr(const CXXTypeidExpr *E) {
6021-
if (!E->isPotentiallyEvaluated())
6022-
return Success(E);
6032+
if (!E->isPotentiallyEvaluated()) {
6033+
TypeInfoLValue TypeInfo;
6034+
if (E->isTypeOperand())
6035+
TypeInfo = TypeInfoLValue(E->getTypeOperand(Info.Ctx).getTypePtr());
6036+
else
6037+
TypeInfo = TypeInfoLValue(E->getExprOperand()->getType().getTypePtr());
6038+
return Success(APValue::LValueBase::getTypeInfo(TypeInfo, E->getType()));
6039+
}
60236040

60246041
Info.FFDiag(E, diag::note_constexpr_typeid_polymorphic)
60256042
<< E->getExprOperand()->getType()
@@ -6615,9 +6632,11 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
66156632
if (const ValueDecl *VD =
66166633
OffsetResult.Base.dyn_cast<const ValueDecl*>()) {
66176634
BaseAlignment = Info.Ctx.getDeclAlign(VD);
6635+
} else if (const Expr *E = OffsetResult.Base.dyn_cast<const Expr *>()) {
6636+
BaseAlignment = GetAlignOfExpr(Info, E, UETT_AlignOf);
66186637
} else {
6619-
BaseAlignment = GetAlignOfExpr(
6620-
Info, OffsetResult.Base.get<const Expr *>(), UETT_AlignOf);
6638+
BaseAlignment = GetAlignOfType(
6639+
Info, OffsetResult.Base.getTypeInfoType(), UETT_AlignOf);
66216640
}
66226641

66236642
if (BaseAlignment < Align) {
@@ -8335,6 +8354,10 @@ static bool EvaluateBuiltinConstantPForLValue(const APValue &LV) {
83358354
if (!isa<StringLiteral>(E))
83368355
return false;
83378356
return LV.getLValueOffset().isZero();
8357+
} else if (Base.is<TypeInfoLValue>()) {
8358+
// Surprisingly, GCC considers __builtin_constant_p(&typeid(int)) to
8359+
// evaluate to true.
8360+
return true;
83388361
} else {
83398362
// Any other base is not constant enough for GCC.
83408363
return false;
@@ -8399,6 +8422,8 @@ static QualType getObjectType(APValue::LValueBase B) {
83998422
} else if (const Expr *E = B.get<const Expr*>()) {
84008423
if (isa<CompoundLiteralExpr>(E))
84018424
return E->getType();
8425+
} else if (B.is<TypeInfoLValue>()) {
8426+
return B.getTypeInfoType();
84028427
}
84038428

84048429
return QualType();

0 commit comments

Comments
 (0)