This revision was not accepted when it landed; it landed in state "Needs 
Review".
This revision was automatically updated to reflect the committed changes.
Closed by commit rC337422: DR330: when determining whether a cast casts away 
constness, consider (authored by rsmith, committed by ).

Repository:
  rC Clang

https://reviews.llvm.org/D49457

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

Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h
+++ include/clang/AST/ASTContext.h
@@ -2289,6 +2289,7 @@
                            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
Index: test/CXX/drs/dr3xx.cpp
===================================================================
--- test/CXX/drs/dr3xx.cpp
+++ test/CXX/drs/dr3xx.cpp
@@ -403,6 +403,28 @@
     (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
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -5008,28 +5008,29 @@
 /// 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,16 +5047,16 @@
 /// \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>();
   if (T1PtrType && T2PtrType) {
     T1 = T1PtrType->getPointeeType();
     T2 = T2PtrType->getPointeeType();
     return true;
   }
-  
+
   const auto *T1MPType = T1->getAs<MemberPointerType>();
   const auto *T2MPType = T2->getAs<MemberPointerType>();
   if (T1MPType && T2MPType && 
Index: lib/Sema/SemaCast.cpp
===================================================================
--- lib/Sema/SemaCast.cpp
+++ lib/Sema/SemaCast.cpp
@@ -459,50 +459,83 @@
 /// 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
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D49457: D... Richard Smith - zygoloid via Phabricator via cfe-commits
    • [PATCH] D494... John McCall via Phabricator via cfe-commits
    • [PATCH] D494... Richard Smith - zygoloid via Phabricator via cfe-commits
    • [PATCH] D494... John McCall via Phabricator via cfe-commits
    • [PATCH] D494... David Zarzycki via Phabricator via cfe-commits
    • [PATCH] D494... Richard Smith - zygoloid via Phabricator via cfe-commits

Reply via email to