From: Richard Smith Date: Fri, 17 May 2019 01:46:05 +0000 (+0000) Subject: Refactor constant evaluation of typeid(T) to track a symbolic type_info X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=072c4a550c5f76bd48d5fc90d34c90abceb56561;p=clang 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.) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@360974 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/APValue.h b/include/clang/AST/APValue.h index d266473068..a6b0fc51cb 100644 --- a/include/clang/AST/APValue.h +++ b/include/clang/AST/APValue.h @@ -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(T); } + static TypeInfoLValue getFromOpaqueValue(void *Value) { + TypeInfoLValue V; + V.T = reinterpret_cast(Value); + return V; + } + + void print(llvm::raw_ostream &Out, const PrintingPolicy &Policy) const; +}; +} + +namespace llvm { +template<> struct PointerLikeTypeTraits { + 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] @@ -57,13 +95,18 @@ public: class LValueBase { public: - typedef llvm::PointerUnion PtrTy; + typedef llvm::PointerUnion + PtrTy; - LValueBase() : CallIndex(0), Version(0) {} + LValueBase() : Local{} {} template - LValueBase(T P, unsigned I = 0, unsigned V = 0) - : Ptr(P), CallIndex(I), Version(V) {} + LValueBase(T P, unsigned I = 0, unsigned V = 0) : Ptr(P), Local{I, V} { + assert(!is() && + "don't use this constructor to form a type_info lvalue"); + } + + static LValueBase getTypeInfo(TypeInfoLValue LV, QualType TypeInfo); template bool is() const { return Ptr.is(); } @@ -78,28 +121,15 @@ public: bool isNull() const; - explicit operator bool () const; + explicit operator bool() const; - PtrTy getPointer() const { - return Ptr; - } - - unsigned getCallIndex() const { - return CallIndex; - } + PtrTy getPointer() const { return Ptr; } - void setCallIndex(unsigned Index) { - CallIndex = Index; - } + unsigned getCallIndex() const; + unsigned getVersion() const; + QualType getTypeInfoType() const; - unsigned getVersion() const { - return Version; - } - - 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); } @@ -107,7 +137,14 @@ public: 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 diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index 17256fbd80..3e9ebbbabd 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -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">; diff --git a/lib/AST/APValue.cpp b/lib/AST/APValue.cpp index e7902e6878..83b0fb5e1a 100644 --- a/lib/AST/APValue.cpp +++ b/lib/AST/APValue.cpp @@ -20,6 +20,56 @@ #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::NumLowBitsAvailable <= + alignof(const Type *), + "Type is insufficiently aligned"); + +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() ? 0 : Local.CallIndex; +} + +unsigned APValue::LValueBase::getVersion() const { + return is() ? 0 : Local.Version; +} + +QualType APValue::LValueBase::getTypeInfoType() const { + assert(is() && "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()) + return true; + return LHS.Local.CallIndex == RHS.Local.CallIndex && + LHS.Local.Version == RHS.Local.Version; +} +} + namespace { struct LVBase { APValue::LValueBase Base; @@ -60,6 +110,8 @@ llvm::DenseMapInfo::getTombstoneKey() { namespace clang { llvm::hash_code hash_value(const APValue::LValueBase &Base) { + if (Base.is()) + return llvm::hash_value(Base.getOpaqueValue()); return llvm::hash_combine(Base.getOpaqueValue(), Base.getCallIndex(), Base.getVersion()); } @@ -470,7 +522,9 @@ void APValue::printPretty(raw_ostream &Out, ASTContext &Ctx, QualType Ty) const{ if (const ValueDecl *VD = Base.dyn_cast()) Out << *VD; - else { + else if (TypeInfoLValue TI = Base.dyn_cast()) { + TI.print(Out, Ctx.getPrintingPolicy()); + } else { assert(Base.get() != nullptr && "Expecting non-null Expr"); Base.get()->printPretty(Out, nullptr, @@ -495,6 +549,9 @@ void APValue::printPretty(raw_ostream &Out, ASTContext &Ctx, QualType Ty) const{ if (const ValueDecl *VD = Base.dyn_cast()) { Out << *VD; ElemTy = VD->getType(); + } else if (TypeInfoLValue TI = Base.dyn_cast()) { + TI.print(Out, Ctx.getPrintingPolicy()); + ElemTy = Base.getTypeInfoType(); } else { const Expr *E = Base.get(); assert(E != nullptr && "Expecting non-null Expr"); diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 29b6d12c2f..e41264e55e 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -87,6 +87,9 @@ namespace { return D->getType(); } + if (TypeInfoLValue TI = B.dyn_cast()) + return B.getTypeInfoType(); + const Expr *Base = B.get(); // For a materialized temporary, the type of the temporary we materialized @@ -1783,6 +1786,9 @@ static bool IsGlobalLValue(APValue::LValueBase B) { return isa(D); } + if (B.is()) + return true; + const Expr *E = B.get(); switch (E->getStmtClass()) { default: @@ -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: @@ -1878,9 +1883,9 @@ static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) { const ValueDecl *VD = Base.dyn_cast(); if (VD) Info.Note(VD->getLocation(), diag::note_declared_at); - else - Info.Note(Base.get()->getExprLoc(), - diag::note_constexpr_temporary_here); + else if (const Expr *E = Base.dyn_cast()) + 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 @@ -3404,7 +3409,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E, if (!Frame) { if (const MaterializeTemporaryExpr *MTE = - dyn_cast(Base)) { + dyn_cast_or_null(Base)) { assert(MTE->getStorageDuration() == SD_Static && "should have a frame for a non-global materialized temporary"); @@ -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 { @@ -5777,13 +5788,13 @@ public: // - 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 @@ -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() @@ -6615,9 +6632,11 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, if (const ValueDecl *VD = OffsetResult.Base.dyn_cast()) { BaseAlignment = Info.Ctx.getDeclAlign(VD); + } else if (const Expr *E = OffsetResult.Base.dyn_cast()) { + BaseAlignment = GetAlignOfExpr(Info, E, UETT_AlignOf); } else { - BaseAlignment = GetAlignOfExpr( - Info, OffsetResult.Base.get(), UETT_AlignOf); + BaseAlignment = GetAlignOfType( + Info, OffsetResult.Base.getTypeInfoType(), UETT_AlignOf); } if (BaseAlignment < Align) { @@ -8335,6 +8354,10 @@ static bool EvaluateBuiltinConstantPForLValue(const APValue &LV) { if (!isa(E)) return false; return LV.getLValueOffset().isZero(); + } else if (Base.is()) { + // 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; @@ -8399,6 +8422,8 @@ static QualType getObjectType(APValue::LValueBase B) { } else if (const Expr *E = B.get()) { if (isa(E)) return E->getType(); + } else if (B.is()) { + return B.getTypeInfoType(); } return QualType(); diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp index dd71cf02d9..70904b192a 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/lib/CodeGen/CGExprConstant.cpp @@ -1735,6 +1735,17 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { return nullptr; } + // Handle typeid(T). + if (TypeInfoLValue TI = base.dyn_cast()) { + llvm::Type *StdTypeInfoPtrTy = + CGM.getTypes().ConvertType(base.getTypeInfoType())->getPointerTo(); + llvm::Constant *TypeInfo = + CGM.GetAddrOfRTTIDescriptor(QualType(TI.getType(), 0)); + if (TypeInfo->getType() != StdTypeInfoPtrTy) + TypeInfo = llvm::ConstantExpr::getBitCast(TypeInfo, StdTypeInfoPtrTy); + return TypeInfo; + } + // Otherwise, it must be an expression. return Visit(base.get()); } diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index b9d3ff8666..239b4ae795 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -6424,8 +6424,11 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // -- a string literal // -- the result of a typeid expression, or // -- a predefined __func__ variable - if (auto *E = Value.getLValueBase().dyn_cast()) { - if (isa(E)) { + APValue::LValueBase Base = Value.getLValueBase(); + auto *VD = const_cast(Base.dyn_cast()); + if (Base && !VD) { + auto *E = Base.dyn_cast(); + if (E && isa(E)) { Converted = TemplateArgument(ArgResult.get()->IgnoreImpCasts()); break; } @@ -6433,8 +6436,6 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, << Arg->getSourceRange(); return ExprError(); } - auto *VD = const_cast( - Value.getLValueBase().dyn_cast()); // -- a subobject if (Value.hasLValuePath() && Value.getLValuePath().size() == 1 && VD && VD->getType()->isArrayType() && diff --git a/test/CXX/drs/dr19xx.cpp b/test/CXX/drs/dr19xx.cpp index a1e8c76435..4e359681fa 100644 --- a/test/CXX/drs/dr19xx.cpp +++ b/test/CXX/drs/dr19xx.cpp @@ -167,9 +167,14 @@ namespace dr1959 { // dr1959: 3.9 #endif } -namespace dr1968 { // dr1968: yes +namespace dr1968 { // dr1968: no #if __cplusplus >= 201103L - static_assert(&typeid(int) == &typeid(int), ""); // expected-error{{not an integral constant expression}} + // FIXME: According to DR1968, both of these should be considered + // non-constant. + static_assert(&typeid(int) == &typeid(int), ""); + + constexpr const std::type_info *f() { return &typeid(int); } + static_assert(f() == f(), ""); #endif } diff --git a/test/Parser/MicrosoftExtensions.cpp b/test/Parser/MicrosoftExtensions.cpp index 63e8b60823..913645b689 100644 --- a/test/Parser/MicrosoftExtensions.cpp +++ b/test/Parser/MicrosoftExtensions.cpp @@ -138,6 +138,8 @@ typedef COM_CLASS_TEMPLATE_REF COM COM_CLASS_TEMPLATE_REF good_template_arg; COM_CLASS_TEMPLATE bad_template_arg; // expected-error {{non-type template argument of type 'const _GUID' is not a constant expression}} +// expected-note@-1 {{read of object '__uuidof(struct_with_uuid)' whose value is not known}} +// expected-note@-2 {{temporary created here}} namespace PR16911 { struct __declspec(uuid("{12345678-1234-1234-1234-1234567890aB}")) uuid; diff --git a/test/SemaCXX/builtin-constant-p.cpp b/test/SemaCXX/builtin-constant-p.cpp index 21cbaf7c69..f70676d250 100644 --- a/test/SemaCXX/builtin-constant-p.cpp +++ b/test/SemaCXX/builtin-constant-p.cpp @@ -130,3 +130,8 @@ constexpr int mutate6(bool mutate) { static_assert(mutate6(false) == 11); // Mutation of state outside __builtin_constant_p: evaluates to false. static_assert(mutate6(true) == 10); + +// GCC strangely returns true for the address of a type_info object, despite it +// not being a pointer to the start of a string literal. +namespace std { struct type_info; } +static_assert(__builtin_constant_p(&typeid(int))); diff --git a/test/SemaCXX/typeid.cpp b/test/SemaCXX/typeid.cpp index 48fcce0b49..4e696deee1 100644 --- a/test/SemaCXX/typeid.cpp +++ b/test/SemaCXX/typeid.cpp @@ -6,7 +6,7 @@ void f() } namespace std { - class type_info; + struct type_info { const char *name; }; } void g() @@ -27,3 +27,6 @@ void h(int i) { typeid(V); // expected-error{{'typeid' of variably modified type 'char [i]'}} typeid(char [i]); // expected-error{{'typeid' of variably modified type 'char [i]'}} } + +// expected-note@+1 {{read of object 'typeid(int).name' whose value is not known}} +constexpr const char *name = typeid(int).name; // expected-error {{constant expression}} diff --git a/www/cxx_dr_status.html b/www/cxx_dr_status.html index 6592e834eb..663fe584aa 100755 --- a/www/cxx_dr_status.html +++ b/www/cxx_dr_status.html @@ -11623,7 +11623,7 @@ and POD class 1968 NAD Address of typeid in constant expressions - Yes + No 1969