diff options
Diffstat (limited to 'clang')
-rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 6 | ||||
-rw-r--r-- | clang/lib/Sema/SemaCast.cpp | 361 | ||||
-rw-r--r-- | clang/test/CXX/expr/expr.post/expr.reinterpret.cast/p2.cpp | 26 | ||||
-rw-r--r-- | clang/test/Sema/warn-cast-qual.c | 10 |
4 files changed, 223 insertions, 180 deletions
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 0f1ce0e1203..e6d2de17910 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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">; diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 32cb307c864..a2e2950556a 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -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, unsigned msg, CastType castType, } } -/// 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; - } +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, +}; +} - if (T1RefType) { - T1 = T1RefType->getPointeeType(); - // T2 = T2; - return true; - } +/// Unwrap one level of types for CastsAwayConstness. +/// +/// 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; + }; - if (T2RefType) { - // T1 = T1; - T2 = T2RefType->getPointeeType(); - return true; - } + int T1Class = Classify(T1); + if (!T1Class) + return CastAwayConstnessKind::CACK_None; - return false; + 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; } -/// 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. +/// 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 SrcType, QualType DestType, 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 SrcType, QualType DestType, 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)); - } - - // Test if they're compatible. - bool ObjCLifetimeConversion; - return SrcConstruct != DestConstruct && - !Self.IsQualificationConversion(SrcConstruct, DestConstruct, false, - ObjCLifetimeConversion); + 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; + } + + 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(Sema &Self, ExprResult &SrcExpr, 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(Sema &Self, ExprResult &SrcExpr, 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(Sema &Self, ExprResult &SrcExpr, 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(Sema &Self, ExprResult &SrcExpr, // 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(Sema &Self, ExprResult &SrcExpr, 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(Sema &Self, ExprResult &SrcExpr, 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(Sema &Self, ExprResult &SrcExpr, 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(Sema &Self, ExprResult &SrcExpr, // 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(bool FunctionalStyle, /*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(bool FunctionalStyle, } if (Self.getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && - tcr == TC_Success) + isValidCast(tcr)) checkObjCConversion(CCK); if (tcr != TC_Success && msg != 0) { @@ -2342,13 +2346,14 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle, 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, const ExprResult &SrcExpr, 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; diff --git a/clang/test/CXX/expr/expr.post/expr.reinterpret.cast/p2.cpp b/clang/test/CXX/expr/expr.post/expr.reinterpret.cast/p2.cpp new file mode 100644 index 00000000000..b03db27ee1d --- /dev/null +++ b/clang/test/CXX/expr/expr.post/expr.reinterpret.cast/p2.cpp @@ -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 +} diff --git a/clang/test/Sema/warn-cast-qual.c b/clang/test/Sema/warn-cast-qual.c index a682cad75ed..789bde15b13 100644 --- a/clang/test/Sema/warn-cast-qual.c +++ b/clang/test/Sema/warn-cast-qual.c @@ -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}} |