summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/AST/ASTContext.h1
-rw-r--r--lib/AST/ASTContext.cpp21
-rw-r--r--lib/Sema/SemaCast.cpp95
-rw-r--r--test/CXX/drs/dr3xx.cpp22
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