r336738 - Fix determination of whether a reinterpret_cast casts away constness.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 10 16:04:36 PDT 2018


Author: rsmith
Date: Tue Jul 10 16:04:35 2018
New Revision: 336738

URL: http://llvm.org/viewvc/llvm-project?rev=336738&view=rev
Log:
Fix determination of whether a reinterpret_cast casts away constness.

The "casts away constness" check doesn't care at all how the different
layers of the source and destination type were formed: for example, if
the source is a pointer and the destination is a pointer-to-member, the
types are still decomposed and their pointee qualifications are still
checked.

This rule is bizarre and somewhat ridiculous, so as an extension we
accept code making use of such reinterpret_casts with a warning outside
of SFINAE contexts.

Added:
    cfe/trunk/test/CXX/expr/expr.post/expr.reinterpret.cast/p2.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/SemaCast.cpp
    cfe/trunk/test/Sema/warn-cast-qual.c

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=336738&r1=336737&r2=336738&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Jul 10 16:04:35 2018
@@ -6184,6 +6184,12 @@ def err_bad_cxx_cast_bitfield : Error<
 def err_bad_cxx_cast_qualifiers_away : Error<
   "%select{const_cast|static_cast|reinterpret_cast|dynamic_cast|C-style cast|"
   "functional-style cast}0 from %1 to %2 casts away qualifiers">;
+def ext_bad_cxx_cast_qualifiers_away_incoherent : ExtWarn<
+  "ISO C++ does not allow "
+  "%select{const_cast|static_cast|reinterpret_cast|dynamic_cast|C-style cast|"
+  "functional-style cast}0 from %1 to %2 because it casts away qualifiers, "
+  "even though the source and destination types are unrelated">,
+  SFINAEFailure, InGroup<DiagGroup<"cast-qual-unrelated">>;
 def err_bad_const_cast_dest : Error<
   "%select{const_cast||||C-style cast|functional-style cast}0 to %2, "
   "which is not a reference, pointer-to-object, or pointer-to-data-member">;

Modified: cfe/trunk/lib/Sema/SemaCast.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCast.cpp?rev=336738&r1=336737&r2=336738&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCast.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCast.cpp Tue Jul 10 16:04:35 2018
@@ -33,10 +33,16 @@ using namespace clang;
 enum TryCastResult {
   TC_NotApplicable, ///< The cast method is not applicable.
   TC_Success,       ///< The cast method is appropriate and successful.
+  TC_Extension,     ///< The cast method is appropriate and accepted as a
+                    ///< language extension.
   TC_Failed         ///< The cast method is appropriate, but failed. A
                     ///< diagnostic has been emitted.
 };
 
+static bool isValidCast(TryCastResult TCR) {
+  return TCR == TC_Success || TCR == TC_Extension;
+}
+
 enum CastType {
   CT_Const,       ///< const_cast
   CT_Static,      ///< static_cast
@@ -431,95 +437,68 @@ static void diagnoseBadCast(Sema &S, uns
   }
 }
 
-/// UnwrapDissimilarPointerTypes - Like Sema::UnwrapSimilarPointerTypes,
-/// this removes one level of indirection from both types, provided that they're
-/// the same kind of pointer (plain or to-member). Unlike the Sema function,
-/// this one doesn't care if the two pointers-to-member don't point into the
-/// same class. This is because CastsAwayConstness doesn't care.
-/// And additionally, it handles C++ references. If both the types are
-/// references, then their pointee types are returned,
-/// else if only one of them is reference, it's pointee type is returned,
-/// and the other type is returned as-is.
-static bool UnwrapDissimilarPointerTypes(QualType& T1, QualType& T2) {
-  const PointerType *T1PtrType = T1->getAs<PointerType>(),
-                    *T2PtrType = T2->getAs<PointerType>();
-  if (T1PtrType && T2PtrType) {
-    T1 = T1PtrType->getPointeeType();
-    T2 = T2PtrType->getPointeeType();
-    return true;
-  }
-  const ObjCObjectPointerType *T1ObjCPtrType = 
-                                            T1->getAs<ObjCObjectPointerType>(),
-                              *T2ObjCPtrType = 
-                                            T2->getAs<ObjCObjectPointerType>();
-  if (T1ObjCPtrType) {
-    if (T2ObjCPtrType) {
-      T1 = T1ObjCPtrType->getPointeeType();
-      T2 = T2ObjCPtrType->getPointeeType();
-      return true;
-    }
-    else if (T2PtrType) {
-      T1 = T1ObjCPtrType->getPointeeType();
-      T2 = T2PtrType->getPointeeType();
-      return true;
-    }
-  }
-  else if (T2ObjCPtrType) {
-    if (T1PtrType) {
-      T2 = T2ObjCPtrType->getPointeeType();
-      T1 = T1PtrType->getPointeeType();
-      return true;
-    }
-  }
-  
-  const MemberPointerType *T1MPType = T1->getAs<MemberPointerType>(),
-                          *T2MPType = T2->getAs<MemberPointerType>();
-  if (T1MPType && T2MPType) {
-    T1 = T1MPType->getPointeeType();
-    T2 = T2MPType->getPointeeType();
-    return true;
-  }
-  
-  const BlockPointerType *T1BPType = T1->getAs<BlockPointerType>(),
-                         *T2BPType = T2->getAs<BlockPointerType>();
-  if (T1BPType && T2BPType) {
-    T1 = T1BPType->getPointeeType();
-    T2 = T2BPType->getPointeeType();
-    return true;
-  }
-  
-  const LValueReferenceType *T1RefType = T1->getAs<LValueReferenceType>(),
-                            *T2RefType = T2->getAs<LValueReferenceType>();
-  if (T1RefType && T2RefType) {
-    T1 = T1RefType->getPointeeType();
-    T2 = T2RefType->getPointeeType();
-    return true;
-  }
-
-  if (T1RefType) {
-    T1 = T1RefType->getPointeeType();
-    // T2 = T2;
-    return true;
-  }
-
-  if (T2RefType) {
-    // T1 = T1;
-    T2 = T2RefType->getPointeeType();
-    return true;
-  }
-
-  return false;
+namespace {
+/// The kind of unwrapping we did when determining whether a conversion casts
+/// away constness.
+enum CastAwayConstnessKind {
+  /// The conversion does not cast away constness.
+  CACK_None = 0,
+  /// We unwrapped similar types.
+  CACK_Similar = 1,
+  /// We unwrapped dissimilar types with similar representations (eg, a pointer
+  /// versus an Objective-C object pointer).
+  CACK_SimilarKind = 2,
+  /// We unwrapped representationally-unrelated types, such as a pointer versus
+  /// a pointer-to-member.
+  CACK_Incoherent = 3,
+};
 }
 
-/// CastsAwayConstness - Check if the pointer conversion from SrcType to
-/// DestType casts away constness as defined in C++ 5.2.11p8ff. This is used by
-/// the cast checkers.  Both arguments must denote pointer (possibly to member)
-/// types.
+/// Unwrap one level of types for CastsAwayConstness.
 ///
-/// \param CheckCVR Whether to check for const/volatile/restrict qualifiers.
+/// Like Sema::UnwrapSimilarPointerTypes, this removes one level of
+/// indirection from both types, provided that they're both pointer-like.
+/// Unlike the Sema function, doesn't care if the unwrapped pieces are related.
+static CastAwayConstnessKind
+unwrapCastAwayConstnessLevel(ASTContext &Context, QualType &T1, QualType &T2) {
+  if (Context.UnwrapSimilarPointerTypes(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;
+  }
+
+  auto Classify = [](QualType T) {
+    if (T->isAnyPointerType()) return 1;
+    if (T->getAs<MemberPointerType>()) return 2;
+    if (T->getAs<BlockPointerType>()) return 3;
+    return 0;
+  };
+
+  int T1Class = Classify(T1);
+  if (!T1Class)
+    return CastAwayConstnessKind::CACK_None;
+
+  int T2Class = Classify(T2);
+  if (!T2Class)
+    return CastAwayConstnessKind::CACK_None;
+
+  T1 = T1->getPointeeType();
+  T2 = T2->getPointeeType();
+  return T1Class == T2Class ? CastAwayConstnessKind::CACK_SimilarKind
+                            : CastAwayConstnessKind::CACK_Incoherent;
+}
+
+/// Check if the pointer conversion from SrcType to DestType casts away
+/// constness as defined in C++ [expr.const.cast]. This is used by the cast
+/// checkers. Both arguments must denote pointer (possibly to member) types.
 ///
+/// \param CheckCVR Whether to check for const/volatile/restrict qualifiers.
 /// \param CheckObjCLifetime Whether to check Objective-C lifetime qualifiers.
-static bool
+static CastAwayConstnessKind
 CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType,
                    bool CheckCVR, bool CheckObjCLifetime,
                    QualType *TheOffendingSrcType = nullptr,
@@ -527,33 +506,35 @@ CastsAwayConstness(Sema &Self, QualType
                    Qualifiers *CastAwayQualifiers = nullptr) {
   // If the only checking we care about is for Objective-C lifetime qualifiers,
   // and we're not in ObjC mode, there's nothing to check.
-  if (!CheckCVR && CheckObjCLifetime && 
-      !Self.Context.getLangOpts().ObjC1)
-    return false;
-    
-  // Casting away constness is defined in C++ 5.2.11p8 with reference to
-  // C++ 4.4. We piggyback on Sema::IsQualificationConversion for this, since
-  // the rules are non-trivial. So first we construct Tcv *...cv* as described
-  // in C++ 5.2.11p8.
-  assert((SrcType->isAnyPointerType() || SrcType->isMemberPointerType() ||
-          SrcType->isBlockPointerType() ||
-          DestType->isLValueReferenceType()) &&
-         "Source type is not pointer or pointer to member.");
-  assert((DestType->isAnyPointerType() || DestType->isMemberPointerType() ||
-          DestType->isBlockPointerType() ||
-          DestType->isLValueReferenceType()) &&
-         "Destination type is not pointer or pointer to member, or reference.");
+  if (!CheckCVR && CheckObjCLifetime && !Self.Context.getLangOpts().ObjC1)
+    return CastAwayConstnessKind::CACK_None;
+
+  if (!DestType->isReferenceType()) {
+    assert((SrcType->isAnyPointerType() || SrcType->isMemberPointerType() ||
+            SrcType->isBlockPointerType()) &&
+           "Source type is not pointer or pointer to member.");
+    assert((DestType->isAnyPointerType() || DestType->isMemberPointerType() ||
+            DestType->isBlockPointerType()) &&
+           "Destination type is not pointer or pointer to member.");
+  }
 
   QualType UnwrappedSrcType = Self.Context.getCanonicalType(SrcType), 
            UnwrappedDestType = Self.Context.getCanonicalType(DestType);
-  SmallVector<Qualifiers, 8> cv1, cv2;
 
   // Find the qualifiers. We only care about cvr-qualifiers for the 
   // purpose of this check, because other qualifiers (address spaces, 
   // Objective-C GC, etc.) are part of the type's identity.
   QualType PrevUnwrappedSrcType = UnwrappedSrcType;
   QualType PrevUnwrappedDestType = UnwrappedDestType;
-  while (UnwrapDissimilarPointerTypes(UnwrappedSrcType, UnwrappedDestType)) {
+  auto WorstKind = CastAwayConstnessKind::CACK_Similar;
+  bool AllConstSoFar = true;
+  while (auto Kind = unwrapCastAwayConstnessLevel(
+             Self.Context, UnwrappedSrcType, UnwrappedDestType)) {
+    // Track the worst kind of unwrap we needed to do before we found a
+    // problem.
+    if (Kind > WorstKind)
+      WorstKind = Kind;
+
     // Determine the relevant qualifiers at this level.
     Qualifiers SrcQuals, DestQuals;
     Self.Context.getUnqualifiedArrayType(UnwrappedSrcType, SrcQuals);
@@ -566,51 +547,71 @@ CastsAwayConstness(Sema &Self, QualType
         UnwrappedDestType->isObjCObjectType())
       SrcQuals.removeConst();
 
-    Qualifiers RetainedSrcQuals, RetainedDestQuals;
     if (CheckCVR) {
-      RetainedSrcQuals.setCVRQualifiers(SrcQuals.getCVRQualifiers());
-      RetainedDestQuals.setCVRQualifiers(DestQuals.getCVRQualifiers());
+      Qualifiers SrcCvrQuals =
+          Qualifiers::fromCVRMask(SrcQuals.getCVRQualifiers());
+      Qualifiers DestCvrQuals =
+          Qualifiers::fromCVRMask(DestQuals.getCVRQualifiers());
+
+      if (SrcCvrQuals != DestCvrQuals) {
+        if (CastAwayQualifiers)
+          *CastAwayQualifiers = SrcCvrQuals - DestCvrQuals;
+
+        // If we removed a cvr-qualifier, this is casting away 'constness'.
+        if (!DestCvrQuals.compatiblyIncludes(SrcCvrQuals)) {
+          if (TheOffendingSrcType)
+            *TheOffendingSrcType = PrevUnwrappedSrcType;
+          if (TheOffendingDestType)
+            *TheOffendingDestType = PrevUnwrappedDestType;
+          return WorstKind;
+        }
 
-      if (RetainedSrcQuals != RetainedDestQuals && TheOffendingSrcType &&
-          TheOffendingDestType && CastAwayQualifiers) {
-        *TheOffendingSrcType = PrevUnwrappedSrcType;
-        *TheOffendingDestType = PrevUnwrappedDestType;
-        *CastAwayQualifiers = RetainedSrcQuals - RetainedDestQuals;
+        // If any prior level was not 'const', this is also casting away
+        // 'constness'. We noted the outermost type missing a 'const' already.
+        if (!AllConstSoFar)
+          return WorstKind;
       }
     }
-    
+
     if (CheckObjCLifetime &&
         !DestQuals.compatiblyIncludesObjCLifetime(SrcQuals))
-      return true;
-    
-    cv1.push_back(RetainedSrcQuals);
-    cv2.push_back(RetainedDestQuals);
+      return WorstKind;
+
+    // If we found our first non-const-qualified type, this may be the place
+    // where things start to go wrong.
+    if (AllConstSoFar && !DestQuals.hasConst()) {
+      AllConstSoFar = false;
+      if (TheOffendingSrcType)
+        *TheOffendingSrcType = PrevUnwrappedSrcType;
+      if (TheOffendingDestType)
+        *TheOffendingDestType = PrevUnwrappedDestType;
+    }
 
     PrevUnwrappedSrcType = UnwrappedSrcType;
     PrevUnwrappedDestType = UnwrappedDestType;
   }
-  if (cv1.empty())
-    return false;
 
-  // Construct void pointers with those qualifiers (in reverse order of
-  // unwrapping, of course).
-  QualType SrcConstruct = Self.Context.VoidTy;
-  QualType DestConstruct = Self.Context.VoidTy;
-  ASTContext &Context = Self.Context;
-  for (SmallVectorImpl<Qualifiers>::reverse_iterator i1 = cv1.rbegin(),
-                                                     i2 = cv2.rbegin();
-       i1 != cv1.rend(); ++i1, ++i2) {
-    SrcConstruct
-      = Context.getPointerType(Context.getQualifiedType(SrcConstruct, *i1));
-    DestConstruct
-      = Context.getPointerType(Context.getQualifiedType(DestConstruct, *i2));
+  return CastAwayConstnessKind::CACK_None;
+}
+
+static TryCastResult getCastAwayConstnessCastKind(CastAwayConstnessKind CACK,
+                                                  unsigned &DiagID) {
+  switch (CACK) {
+  case CastAwayConstnessKind::CACK_None:
+    llvm_unreachable("did not cast away constness");
+
+  case CastAwayConstnessKind::CACK_Similar:
+    // FIXME: Accept these as an extension too?
+  case CastAwayConstnessKind::CACK_SimilarKind:
+    DiagID = diag::err_bad_cxx_cast_qualifiers_away;
+    return TC_Failed;
+
+  case CastAwayConstnessKind::CACK_Incoherent:
+    DiagID = diag::ext_bad_cxx_cast_qualifiers_away_incoherent;
+    return TC_Extension;
   }
 
-  // Test if they're compatible.
-  bool ObjCLifetimeConversion;
-  return SrcConstruct != DestConstruct &&
-    !Self.IsQualificationConversion(SrcConstruct, DestConstruct, false,
-                                    ObjCLifetimeConversion);
+  llvm_unreachable("unexpected cast away constness kind");
 }
 
 /// CheckDynamicCast - Check that a dynamic_cast\<DestType\>(SrcExpr) is valid.
@@ -778,12 +779,13 @@ void CastOperation::CheckConstCast() {
     return;
 
   unsigned msg = diag::err_bad_cxx_cast_generic;
-  if (TryConstCast(Self, SrcExpr, DestType, /*CStyle*/false, msg) != TC_Success
-      && msg != 0) {
+  auto TCR = TryConstCast(Self, SrcExpr, DestType, /*CStyle*/ false, msg);
+  if (TCR != TC_Success && msg != 0) {
     Self.Diag(OpRange.getBegin(), msg) << CT_Const
       << SrcExpr.get()->getType() << DestType << OpRange;
-    SrcExpr = ExprError();
   }
+  if (!isValidCast(TCR))
+    SrcExpr = ExprError();
 }
 
 /// Check that a reinterpret_cast\<DestType\>(SrcExpr) is not used as upcast
@@ -896,8 +898,7 @@ void CastOperation::CheckReinterpretCast
   TryCastResult tcr = 
     TryReinterpretCast(Self, SrcExpr, DestType, 
                        /*CStyle*/false, OpRange, msg, Kind);
-  if (tcr != TC_Success && msg != 0)
-  {
+  if (tcr != TC_Success && msg != 0) {
     if (SrcExpr.isInvalid()) // if conversion failed, don't report another error
       return;
     if (SrcExpr.get()->getType() == Self.Context.OverloadTy) {
@@ -911,11 +912,14 @@ void CastOperation::CheckReinterpretCast
       diagnoseBadCast(Self, msg, CT_Reinterpret, OpRange, SrcExpr.get(),
                       DestType, /*listInitialization=*/false);
     }
-    SrcExpr = ExprError();
-  } else if (tcr == TC_Success) {
+  }
+
+  if (isValidCast(tcr)) {
     if (Self.getLangOpts().allowsNonTrivialObjCLifetimeQualifiers())
       checkObjCConversion(Sema::CCK_OtherCast);
     DiagnoseReinterpretUpDownCast(Self, SrcExpr.get(), DestType, OpRange);
+  } else {
+    SrcExpr = ExprError();
   }
 }
 
@@ -973,14 +977,15 @@ void CastOperation::CheckStaticCast() {
       diagnoseBadCast(Self, msg, CT_Static, OpRange, SrcExpr.get(), DestType,
                       /*listInitialization=*/false);
     }
-    SrcExpr = ExprError();
-  } else if (tcr == TC_Success) {
+  }
+
+  if (isValidCast(tcr)) {
     if (Kind == CK_BitCast)
       checkCastAlign();
     if (Self.getLangOpts().allowsNonTrivialObjCLifetimeQualifiers())
       checkObjCConversion(Sema::CCK_OtherCast);
-  } else if (Kind == CK_BitCast) {
-    checkCastAlign();
+  } else {
+    SrcExpr = ExprError();
   }
 }
 
@@ -2000,16 +2005,6 @@ static TryCastResult TryReinterpretCast(
         SrcMemPtr->isMemberFunctionPointer())
       return TC_NotApplicable;
 
-    // C++ 5.2.10p2: The reinterpret_cast operator shall not cast away
-    //   constness.
-    // A reinterpret_cast followed by a const_cast can, though, so in C-style,
-    // we accept it.
-    if (CastsAwayConstness(Self, SrcType, DestType, /*CheckCVR=*/!CStyle,
-                           /*CheckObjCLifetime=*/CStyle)) {
-      msg = diag::err_bad_cxx_cast_qualifiers_away;
-      return TC_Failed;
-    }
-
     if (Self.Context.getTargetInfo().getCXXABI().isMicrosoft()) {
       // We need to determine the inheritance model that the class will use if
       // haven't yet.
@@ -2024,6 +2019,15 @@ static TryCastResult TryReinterpretCast(
       return TC_Failed;
     }
 
+    // C++ 5.2.10p2: The reinterpret_cast operator shall not cast away
+    //   constness.
+    // A reinterpret_cast followed by a const_cast can, though, so in C-style,
+    // we accept it.
+    if (auto CACK =
+            CastsAwayConstness(Self, SrcType, DestType, /*CheckCVR=*/!CStyle,
+                               /*CheckObjCLifetime=*/CStyle))
+      return getCastAwayConstnessCastKind(CACK, msg);
+
     // A valid member pointer cast.
     assert(!IsLValueCast);
     Kind = CK_ReinterpretMemberPointer;
@@ -2140,19 +2144,19 @@ static TryCastResult TryReinterpretCast(
     return TC_NotApplicable;
   }
 
-  // C++ 5.2.10p2: The reinterpret_cast operator shall not cast away constness.
-  // The C-style cast operator can.
-  if (CastsAwayConstness(Self, SrcType, DestType, /*CheckCVR=*/!CStyle,
-                         /*CheckObjCLifetime=*/CStyle)) {
-    msg = diag::err_bad_cxx_cast_qualifiers_away;
-    return TC_Failed;
-  }
-  
   // Cannot convert between block pointers and Objective-C object pointers.
   if ((SrcType->isBlockPointerType() && DestType->isObjCObjectPointerType()) ||
       (DestType->isBlockPointerType() && SrcType->isObjCObjectPointerType()))
     return TC_NotApplicable;
 
+  // C++ 5.2.10p2: The reinterpret_cast operator shall not cast away constness.
+  // The C-style cast operator can.
+  TryCastResult SuccessResult = TC_Success;
+  if (auto CACK =
+          CastsAwayConstness(Self, SrcType, DestType, /*CheckCVR=*/!CStyle,
+                             /*CheckObjCLifetime=*/CStyle))
+    SuccessResult = getCastAwayConstnessCastKind(CACK, msg);
+
   if (IsLValueCast) {
     Kind = CK_LValueBitCast;
   } else if (DestType->isObjCObjectPointerType()) {
@@ -2170,7 +2174,7 @@ static TryCastResult TryReinterpretCast(
   // Any pointer can be cast to an Objective-C pointer type with a C-style
   // cast.
   if (CStyle && DestType->isObjCObjectPointerType()) {
-    return TC_Success;
+    return SuccessResult;
   }
   if (CStyle)
     DiagnoseCastOfObjCSEL(Self, SrcExpr, DestType);
@@ -2184,7 +2188,7 @@ static TryCastResult TryReinterpretCast(
     if (DestType->isFunctionPointerType()) {
       // C++ 5.2.10p6: A pointer to a function can be explicitly converted to
       // a pointer to a function of a different type.
-      return TC_Success;
+      return SuccessResult;
     }
 
     // C++0x 5.2.10p8: Converting a pointer to a function into a pointer to
@@ -2197,7 +2201,7 @@ static TryCastResult TryReinterpretCast(
               Self.getLangOpts().CPlusPlus11 ?
                 diag::warn_cxx98_compat_cast_fn_obj : diag::ext_cast_fn_obj)
       << OpRange;
-    return TC_Success;
+    return SuccessResult;
   }
 
   if (DestType->isFunctionPointerType()) {
@@ -2206,7 +2210,7 @@ static TryCastResult TryReinterpretCast(
               Self.getLangOpts().CPlusPlus11 ?
                 diag::warn_cxx98_compat_cast_fn_obj : diag::ext_cast_fn_obj)
       << OpRange;
-    return TC_Success;
+    return SuccessResult;
   }
   
   // C++ 5.2.10p7: A pointer to an object can be explicitly converted to
@@ -2214,8 +2218,8 @@ static TryCastResult TryReinterpretCast(
   // Void pointers are not specified, but supported by every compiler out there.
   // So we finish by allowing everything that remains - it's got to be two
   // object pointers.
-  return TC_Success;
-}                                     
+  return SuccessResult;
+}
 
 void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle,
                                        bool ListInitialization) {
@@ -2295,7 +2299,7 @@ void CastOperation::CheckCXXCStyleCast(b
                                    /*CStyle*/true, msg);
   if (SrcExpr.isInvalid())
     return;
-  if (tcr == TC_Success)
+  if (isValidCast(tcr))
     Kind = CK_NoOp;
 
   Sema::CheckedConversionKind CCK
@@ -2318,7 +2322,7 @@ void CastOperation::CheckCXXCStyleCast(b
   }
 
   if (Self.getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() &&
-      tcr == TC_Success)
+      isValidCast(tcr))
     checkObjCConversion(CCK);
 
   if (tcr != TC_Success && msg != 0) {
@@ -2342,13 +2346,14 @@ void CastOperation::CheckCXXCStyleCast(b
       diagnoseBadCast(Self, msg, (FunctionalStyle ? CT_Functional : CT_CStyle),
                       OpRange, SrcExpr.get(), DestType, ListInitialization);
     }
-  } else if (Kind == CK_BitCast) {
-    checkCastAlign();
   }
 
-  // Clear out SrcExpr if there was a fatal error.
-  if (tcr != TC_Success)
+  if (isValidCast(tcr)) {
+    if (Kind == CK_BitCast)
+      checkCastAlign();
+  } else {
     SrcExpr = ExprError();
+  }
 }
 
 /// DiagnoseBadFunctionCast - Warn whenever a function call is cast to a 
@@ -2633,11 +2638,13 @@ static void DiagnoseCastQual(Sema &Self,
 
   QualType TheOffendingSrcType, TheOffendingDestType;
   Qualifiers CastAwayQualifiers;
-  if (!CastsAwayConstness(Self, SrcType, DestType, true, false,
-                          &TheOffendingSrcType, &TheOffendingDestType,
-                          &CastAwayQualifiers))
+  if (CastsAwayConstness(Self, SrcType, DestType, true, false,
+                         &TheOffendingSrcType, &TheOffendingDestType,
+                         &CastAwayQualifiers) !=
+      CastAwayConstnessKind::CACK_Similar)
     return;
 
+  // FIXME: 'restrict' is not properly handled here.
   int qualifiers = -1;
   if (CastAwayQualifiers.hasConst() && CastAwayQualifiers.hasVolatile()) {
     qualifiers = 0;

Added: cfe/trunk/test/CXX/expr/expr.post/expr.reinterpret.cast/p2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.post/expr.reinterpret.cast/p2.cpp?rev=336738&view=auto
==============================================================================
--- cfe/trunk/test/CXX/expr/expr.post/expr.reinterpret.cast/p2.cpp (added)
+++ cfe/trunk/test/CXX/expr/expr.post/expr.reinterpret.cast/p2.cpp Tue Jul 10 16:04:35 2018
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 %s -verify
+
+// The reinterpret_cast operator shall not cast away constness.
+struct X {};
+struct Y {};
+void f(const int * X::* Y::* *p) {
+  // This applies for similar types...
+  (void)reinterpret_cast<int * X::* Y::* *>(p); // expected-error {{casts away qualifiers}}
+  // ... and for cases where the base type is different ...
+  (void)reinterpret_cast<float * X::* Y::* *>(p); // expected-error {{casts away qualifiers}}
+  // ... and for cases where pointers to members point to members of different classes ...
+  (void)reinterpret_cast<int * Y::* X::* *>(p); // expected-error {{casts away qualifiers}}
+  // ... and even for cases where the path is wholly different!
+  // (Though we accept such cases as an extension.)
+  (void)reinterpret_cast<double Y::* X::* * *>(p); // expected-warning {{casts away qualifiers}}
+
+  // If qualifiers are added, we need a 'const' at every level above.
+  (void)reinterpret_cast<const volatile double Y::* X::* * *>(p); // expected-warning {{casts away qualifiers}}
+  (void)reinterpret_cast<const volatile double Y::*const X::*const **>(p); // expected-warning {{casts away qualifiers}}
+  (void)reinterpret_cast<const volatile double Y::*const X::**const *>(p); // expected-warning {{casts away qualifiers}}
+  (void)reinterpret_cast<const volatile double Y::*X::*const *const *>(p); // expected-warning {{casts away qualifiers}}
+  (void)reinterpret_cast<const volatile double Y::*const X::*const *const *>(p); // ok
+
+  (void)reinterpret_cast<const double Y::*volatile X::**const *>(p); // expected-warning {{casts away qualifiers}}
+  (void)reinterpret_cast<const double Y::*volatile X::*const *const *>(p); // ok
+}

Modified: cfe/trunk/test/Sema/warn-cast-qual.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/warn-cast-qual.c?rev=336738&r1=336737&r2=336738&view=diff
==============================================================================
--- cfe/trunk/test/Sema/warn-cast-qual.c (original)
+++ cfe/trunk/test/Sema/warn-cast-qual.c Tue Jul 10 16:04:35 2018
@@ -4,11 +4,15 @@
 #include <stdint.h>
 
 void foo() {
-  const char * const ptr = 0;
-  const char * const *ptrptr = 0;
+  const char *const ptr = 0;
+  const char *const *ptrptr = 0;
+  char *const *ptrcptr = 0;
+  char **ptrptr2 = 0;
   char *y = (char *)ptr;	// expected-warning {{cast from 'const char *' to 'char *' drops const qualifier}}
-  char **y1 = (char **)ptrptr;	// expected-warning {{cast from 'const char *const' to 'char *' drops const qualifier}}
+  char **y1 = (char **)ptrptr;	// expected-warning {{cast from 'const char *const *' to 'char **' drops const qualifier}}
   const char **y2 = (const char **)ptrptr;	// expected-warning {{cast from 'const char *const *' to 'const char **' drops const qualifier}}
+  char *const *y3 = (char *const *)ptrptr;	// expected-warning {{cast from 'const char *const' to 'char *const' drops const qualifier}}
+  const char **y4 = (const char **)ptrcptr;	// expected-warning {{cast from 'char *const *' to 'const char **' drops const qualifier}}
 
   char *z = (char *)(uintptr_t)(const void *)ptr;	// no warning
   char *z1 = (char *)(const void *)ptr;	// expected-warning {{cast from 'const void *' to 'char *' drops const qualifier}}




More information about the cfe-commits mailing list