]> granicus.if.org Git - clang/commitdiff
Refactor constant evaluation of typeid(T) to track a symbolic type_info
authorRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 17 May 2019 07:06:46 +0000 (07:06 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 17 May 2019 07:06:46 +0000 (07:06 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@360995 91177308-0d34-0410-b5e6-96231b3b80d8

12 files changed:
include/clang/AST/APValue.h
include/clang/AST/Type.h
include/clang/Basic/DiagnosticASTKinds.td
lib/AST/APValue.cpp
lib/AST/ExprConstant.cpp
lib/CodeGen/CGExprConstant.cpp
lib/Sema/SemaTemplate.cpp
test/CXX/drs/dr19xx.cpp
test/Parser/MicrosoftExtensions.cpp
test/SemaCXX/builtin-constant-p.cpp
test/SemaCXX/typeid.cpp
www/cxx_dr_status.html

index d266473068424534f394d9962fe025cf309e0f77..15f8944e4f51c58706d061759978578fd613af6d 100644 (file)
@@ -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]
@@ -56,14 +94,14 @@ public:
   };
 
   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>(); }
@@ -78,28 +116,13 @@ public:
 
     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);
     }
@@ -107,7 +130,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
index 0633924f108a5833206dfd835285a5b87df1a3bc..7c457ccee1967d842855ea5773f8d2de4d43393a 100644 (file)
@@ -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,
index 17256fbd80b0500c1790067b24ef9ee7a3bba567..3e9ebbbabd45ea963085274532a7a81f740ef981 100644 (file)
@@ -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">;
index e7902e6878008935117d745b35b15fbec123bf82..249c382f893eb039070eafbd99af78745b4ae5c0 100644 (file)
 #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;
@@ -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());
 }
@@ -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,
@@ -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");
index 29b6d12c2f276f66ae9920af60ade2cdd125f208..e41264e55e4c9fe9b4a3729e1d33def982688cda 100644 (file)
@@ -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
@@ -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:
@@ -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<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
@@ -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");
 
@@ -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<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) {
@@ -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;
@@ -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();
index dd71cf02d957bb677ffbfec957143adb668182fd..70904b192a39ac8314eb189ca92e2946e132775c 100644 (file)
@@ -1735,6 +1735,17 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
     return nullptr;
   }
 
+  // Handle typeid(T).
+  if (TypeInfoLValue TI = base.dyn_cast<TypeInfoLValue>()) {
+    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<const Expr*>());
 }
index b9d3ff8666f9024e728753df33855c0748a4f3b1..239b4ae7957e821184409ef27d07f705d4f25fcc 100644 (file)
@@ -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<const Expr*>()) {
-        if (isa<CXXUuidofExpr>(E)) {
+      APValue::LValueBase Base = Value.getLValueBase();
+      auto *VD = const_cast<ValueDecl *>(Base.dyn_cast<const ValueDecl *>());
+      if (Base && !VD) {
+        auto *E = Base.dyn_cast<const Expr *>();
+        if (E && isa<CXXUuidofExpr>(E)) {
           Converted = TemplateArgument(ArgResult.get()->IgnoreImpCasts());
           break;
         }
@@ -6433,8 +6436,6 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
             << Arg->getSourceRange();
         return ExprError();
       }
-      auto *VD = const_cast<ValueDecl *>(
-          Value.getLValueBase().dyn_cast<const ValueDecl *>());
       // -- a subobject
       if (Value.hasLValuePath() && Value.getLValuePath().size() == 1 &&
           VD && VD->getType()->isArrayType() &&
index a1e8c764354f6d3fe0069a762ab2e5e886031d46..4e359681fa8d9c0df3ca1df68edf4e7c88923ea6 100644 (file)
@@ -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
 }
 
index 63e8b608239e8b32ea915f6afe98c53dbc3f9803..913645b6891b19db33641baa4d012d666468fb6a 100644 (file)
@@ -138,6 +138,8 @@ typedef COM_CLASS_TEMPLATE_REF<struct_with_uuid, __uuidof(struct_with_uuid)> COM
 COM_CLASS_TEMPLATE_REF<int, __uuidof(struct_with_uuid)> good_template_arg;
 
 COM_CLASS_TEMPLATE<int, __uuidof(struct_with_uuid)> 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;
index 21cbaf7c699a8b740bf2a8958cf69a620b9d1dc2..f70676d250e0cadb0fa6807a5d7ee96ee428bda6 100644 (file)
@@ -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)));
index 48fcce0b4924f1c615b841466067537fd82ba154..4e696deee131757e9a3bbc2bd32b0ce3751f245e 100644 (file)
@@ -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}}
index 6592e834ebd8950145bf13fbf18b6f44ba8f7beb..663fe584aaccab34eb0eefaf3ad5050a6ef1c2a6 100755 (executable)
@@ -11623,7 +11623,7 @@ and <I>POD class</I></td>
     <td><a href="http://wg21.link/cwg1968">1968</a></td>
     <td>NAD</td>
     <td>Address of <TT>typeid</TT> in constant expressions</td>
-    <td class="full" align="center">Yes</td>
+    <td class="none" align="center">No</td>
   </tr>
   <tr class="open" id="1969">
     <td><a href="http://wg21.link/cwg1969">1969</a></td>