r337422 - DR330: when determining whether a cast casts away constness, consider

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Wed Jul 18 13:13:36 PDT 2018


Author: rsmith
Date: Wed Jul 18 13:13:36 2018
New Revision: 337422

URL: http://llvm.org/viewvc/llvm-project?rev=337422&view=rev
Log:
DR330: when determining whether a cast casts away constness, consider
qualifiers from all levels matching a multidimensional array.

For example, this allows casting from
  pointer to       array of            array of   const volatile int
to
  pointer to const pointer to volatile pointer to                int
because the multidimensional array part of the source type corresponds
to a part of the destination type that contains both 'const' and
'volatile'.

Differential Revision: https://reviews.llvm.org/D49457

Modified:
    cfe/trunk/include/clang/AST/ASTContext.h
    cfe/trunk/lib/AST/ASTContext.cpp
    cfe/trunk/lib/Sema/SemaCast.cpp
    cfe/trunk/test/CXX/drs/dr3xx.cpp

Modified: cfe/trunk/include/clang/AST/ASTContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=337422&r1=337421&r2=337422&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ASTContext.h (original)
+++ cfe/trunk/include/clang/AST/ASTContext.h Wed Jul 18 13:13:36 2018
@@ -2289,6 +2289,7 @@ public:
                            const ObjCMethodDecl *MethodImp);
 
   bool UnwrapSimilarTypes(QualType &T1, QualType &T2);
+  bool UnwrapSimilarArrayTypes(QualType &T1, QualType &T2);
 
   /// Determine if two types are similar, according to the C++ rules. That is,
   /// determine if they are the same other than qualifiers on the initial

Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=337422&r1=337421&r2=337422&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Wed Jul 18 13:13:36 2018
@@ -5008,28 +5008,29 @@ QualType ASTContext::getUnqualifiedArray
 /// Attempt to unwrap two types that may both be array types with the same bound
 /// (or both be array types of unknown bound) for the purpose of comparing the
 /// cv-decomposition of two types per C++ [conv.qual].
-static void unwrapSimilarArrayTypes(ASTContext &Ctx, QualType &T1,
-                                    QualType &T2) {
+bool ASTContext::UnwrapSimilarArrayTypes(QualType &T1, QualType &T2) {
+  bool UnwrappedAny = false;
   while (true) {
-    auto *AT1 = Ctx.getAsArrayType(T1);
-    if (!AT1) return;
+    auto *AT1 = getAsArrayType(T1);
+    if (!AT1) return UnwrappedAny;
 
-    auto *AT2 = Ctx.getAsArrayType(T2);
-    if (!AT2) return;
+    auto *AT2 = getAsArrayType(T2);
+    if (!AT2) return UnwrappedAny;
 
     // If we don't have two array types with the same constant bound nor two
     // incomplete array types, we've unwrapped everything we can.
     if (auto *CAT1 = dyn_cast<ConstantArrayType>(AT1)) {
       auto *CAT2 = dyn_cast<ConstantArrayType>(AT2);
       if (!CAT2 || CAT1->getSize() != CAT2->getSize())
-        return;
+        return UnwrappedAny;
     } else if (!isa<IncompleteArrayType>(AT1) ||
                !isa<IncompleteArrayType>(AT2)) {
-      return;
+      return UnwrappedAny;
     }
 
     T1 = AT1->getElementType();
     T2 = AT2->getElementType();
+    UnwrappedAny = true;
   }
 }
 
@@ -5046,7 +5047,7 @@ static void unwrapSimilarArrayTypes(ASTC
 /// \return \c true if a pointer type was unwrapped, \c false if we reached a
 /// pair of types that can't be unwrapped further.
 bool ASTContext::UnwrapSimilarTypes(QualType &T1, QualType &T2) {
-  unwrapSimilarArrayTypes(*this, T1, T2);
+  UnwrapSimilarArrayTypes(T1, T2);
 
   const auto *T1PtrType = T1->getAs<PointerType>();
   const auto *T2PtrType = T2->getAs<PointerType>();
@@ -5055,7 +5056,7 @@ bool ASTContext::UnwrapSimilarTypes(Qual
     T2 = T2PtrType->getPointeeType();
     return true;
   }
-  
+
   const auto *T1MPType = T1->getAs<MemberPointerType>();
   const auto *T2MPType = T2->getAs<MemberPointerType>();
   if (T1MPType && T2MPType && 

Modified: cfe/trunk/lib/Sema/SemaCast.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCast.cpp?rev=337422&r1=337421&r2=337422&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCast.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCast.cpp Wed Jul 18 13:13:36 2018
@@ -459,50 +459,83 @@ enum CastAwayConstnessKind {
 /// Like Sema::UnwrapSimilarTypes, this removes one level of indirection from
 /// both types, provided that they're both pointer-like or array-like. Unlike
 /// the Sema function, doesn't care if the unwrapped pieces are related.
+///
+/// This function may remove additional levels as necessary for correctness:
+/// the resulting T1 is unwrapped sufficiently that it is never an array type,
+/// so that its qualifiers can be directly compared to those of T2 (which will
+/// have the combined set of qualifiers from all indermediate levels of T2),
+/// as (effectively) required by [expr.const.cast]p7 replacing T1's qualifiers
+/// with those from T2.
 static CastAwayConstnessKind
 unwrapCastAwayConstnessLevel(ASTContext &Context, QualType &T1, QualType &T2) {
-  // Note, even if this returns false, it may have unwrapped some number of
-  // matching "array of" pieces. That's OK, we don't need to check their
-  // cv-qualifiers (that check is covered by checking the qualifiers on the
-  // array types themselves).
-  if (Context.UnwrapSimilarTypes(T1, T2))
-    return CastAwayConstnessKind::CACK_Similar;
-
-  // Special case: if the destination type is a reference type, unwrap it as
-  // the first level.
-  if (T2->isReferenceType()) {
-    T2 = T2->getPointeeType();
-    return CastAwayConstnessKind::CACK_Similar;
-  }
-
+  enum { None, Ptr, MemPtr, BlockPtr, Array };
   auto Classify = [](QualType T) {
-    if (T->isAnyPointerType()) return 1;
-    if (T->isMemberPointerType()) return 2;
-    if (T->isBlockPointerType()) return 3;
+    if (T->isAnyPointerType()) return Ptr;
+    if (T->isMemberPointerType()) return MemPtr;
+    if (T->isBlockPointerType()) return BlockPtr;
     // We somewhat-arbitrarily don't look through VLA types here. This is at
     // least consistent with the behavior of UnwrapSimilarTypes.
-    if (T->isConstantArrayType() || T->isIncompleteArrayType()) return 4;
-    return 0;
+    if (T->isConstantArrayType() || T->isIncompleteArrayType()) return Array;
+    return None;
   };
 
-  int T1Class = Classify(T1);
-  if (!T1Class)
-    return CastAwayConstnessKind::CACK_None;
-
-  int T2Class = Classify(T2);
-  if (!T2Class)
-    return CastAwayConstnessKind::CACK_None;
-
   auto Unwrap = [&](QualType T) {
     if (auto *AT = Context.getAsArrayType(T))
       return AT->getElementType();
     return T->getPointeeType();
   };
 
-  T1 = Unwrap(T1);
-  T2 = Unwrap(T2);
-  return T1Class == T2Class ? CastAwayConstnessKind::CACK_SimilarKind
-                            : CastAwayConstnessKind::CACK_Incoherent;
+  CastAwayConstnessKind Kind;
+
+  if (T2->isReferenceType()) {
+    // Special case: if the destination type is a reference type, unwrap it as
+    // the first level. (The source will have been an lvalue expression in this
+    // case, so there is no corresponding "reference to" in T1 to remove.) This
+    // simulates removing a "pointer to" from both sides.
+    T2 = T2->getPointeeType();
+    Kind = CastAwayConstnessKind::CACK_Similar;
+  } else if (Context.UnwrapSimilarTypes(T1, T2)) {
+    Kind = CastAwayConstnessKind::CACK_Similar;
+  } else {
+    // Try unwrapping mismatching levels.
+    int T1Class = Classify(T1);
+    if (T1Class == None)
+      return CastAwayConstnessKind::CACK_None;
+
+    int T2Class = Classify(T2);
+    if (T2Class == None)
+      return CastAwayConstnessKind::CACK_None;
+
+    T1 = Unwrap(T1);
+    T2 = Unwrap(T2);
+    Kind = T1Class == T2Class ? CastAwayConstnessKind::CACK_SimilarKind
+                              : CastAwayConstnessKind::CACK_Incoherent;
+  }
+
+  // We've unwrapped at least one level. If the resulting T1 is a (possibly
+  // multidimensional) array type, any qualifier on any matching layer of
+  // T2 is considered to correspond to T1. Decompose down to the element
+  // type of T1 so that we can compare properly.
+  while (true) {
+    Context.UnwrapSimilarArrayTypes(T1, T2);
+
+    if (Classify(T1) != Array)
+      break;
+
+    auto T2Class = Classify(T2);
+    if (T2Class == None)
+      break;
+
+    if (T2Class != Array)
+      Kind = CastAwayConstnessKind::CACK_Incoherent;
+    else if (Kind != CastAwayConstnessKind::CACK_Incoherent)
+      Kind = CastAwayConstnessKind::CACK_SimilarKind;
+
+    T1 = Unwrap(T1);
+    T2 = Unwrap(T2).withCVRQualifiers(T2.getCVRQualifiers());
+  }
+
+  return Kind;
 }
 
 /// Check if the pointer conversion from SrcType to DestType casts away

Modified: cfe/trunk/test/CXX/drs/dr3xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr3xx.cpp?rev=337422&r1=337421&r2=337422&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr3xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr3xx.cpp Wed Jul 18 13:13:36 2018
@@ -403,6 +403,28 @@ namespace dr330 { // dr330: 7
     (void) reinterpret_cast<T>(q); // expected-error {{casts away qualifiers}}
     (void) reinterpret_cast<Q>(t);
   }
+
+  namespace swift_17882 {
+    typedef const char P[72];
+    typedef int *Q;
+    void f(P &pr, P *pp) {
+      (void) reinterpret_cast<const Q&>(pr);
+      (void) reinterpret_cast<const Q*>(pp);
+    }
+
+    struct X {};
+    typedef const volatile int A[1][2][3];
+    typedef int *const X::*volatile *B1;
+    typedef int *const X::*         *B2;
+    typedef int *X::*      volatile *B3;
+    typedef volatile int *(*const B4)[4];
+    void f(A *a) {
+      (void) reinterpret_cast<B1*>(a);
+      (void) reinterpret_cast<B2*>(a); // expected-error {{casts away qualifiers}}
+      (void) reinterpret_cast<B3*>(a); // expected-error {{casts away qualifiers}}
+      (void) reinterpret_cast<B4*>(a);
+    }
+  }
 }
 
 namespace dr331 { // dr331: yes




More information about the cfe-commits mailing list