diff options
-rw-r--r-- | include/clang/AST/ASTContext.h | 1 | ||||
-rw-r--r-- | lib/AST/ASTContext.cpp | 21 | ||||
-rw-r--r-- | lib/Sema/SemaCast.cpp | 95 | ||||
-rw-r--r-- | test/CXX/drs/dr3xx.cpp | 22 |
4 files changed, 98 insertions, 41 deletions
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 8d3cf760a5..c6f8e2973e 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -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 diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index d6bc9c0c71..9cd0844184 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -5008,28 +5008,29 @@ QualType ASTContext::getUnqualifiedArrayType(QualType type, /// 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(ASTContext &Ctx, QualType &T1, /// \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(QualType &T1, QualType &T2) { T2 = T2PtrType->getPointeeType(); return true; } - + const auto *T1MPType = T1->getAs<MemberPointerType>(); const auto *T2MPType = T2->getAs<MemberPointerType>(); if (T1MPType && T2MPType && diff --git a/lib/Sema/SemaCast.cpp b/lib/Sema/SemaCast.cpp index ec95032c4e..86633d6dd5 100644 --- a/lib/Sema/SemaCast.cpp +++ b/lib/Sema/SemaCast.cpp @@ -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 diff --git a/test/CXX/drs/dr3xx.cpp b/test/CXX/drs/dr3xx.cpp index 04fd258a7c..d723c5b78c 100644 --- a/test/CXX/drs/dr3xx.cpp +++ b/test/CXX/drs/dr3xx.cpp @@ -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 |