DR330: look through array types when forming the cv-decomposition of a type.
This allows more qualification conversions, eg. conversion from
'int *(*)[]' -> 'const int *const (*)[]'
is now permitted, along with all the consequences of that: more types
are similar, more cases are permitted by const_cast, and conversely,
fewer "casting away constness" cases are permitted by reinterpret_cast.
llvm-svn: 336745
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index a2e2950..ec95032 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -456,12 +456,16 @@
/// 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.
+/// 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.
static CastAwayConstnessKind
unwrapCastAwayConstnessLevel(ASTContext &Context, QualType &T1, QualType &T2) {
- if (Context.UnwrapSimilarPointerTypes(T1, 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
@@ -473,8 +477,11 @@
auto Classify = [](QualType T) {
if (T->isAnyPointerType()) return 1;
- if (T->getAs<MemberPointerType>()) return 2;
- if (T->getAs<BlockPointerType>()) return 3;
+ if (T->isMemberPointerType()) return 2;
+ if (T->isBlockPointerType()) return 3;
+ // 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;
};
@@ -486,8 +493,14 @@
if (!T2Class)
return CastAwayConstnessKind::CACK_None;
- T1 = T1->getPointeeType();
- T2 = T2->getPointeeType();
+ 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;
}
@@ -1674,29 +1687,14 @@
msg = diag::err_bad_const_cast_dest;
return TC_NotApplicable;
}
- SrcType = Self.Context.getCanonicalType(SrcType);
- // Unwrap the pointers. Ignore qualifiers. Terminate early if the types are
- // completely equal.
- // C++ 5.2.11p3 describes the core semantics of const_cast. All cv specifiers
- // in multi-level pointers may change, but the level count must be the same,
- // as must be the final pointee type.
- while (SrcType != DestType &&
- Self.Context.UnwrapSimilarPointerTypes(SrcType, DestType)) {
- Qualifiers SrcQuals, DestQuals;
- SrcType = Self.Context.getUnqualifiedArrayType(SrcType, SrcQuals);
- DestType = Self.Context.getUnqualifiedArrayType(DestType, DestQuals);
-
- // const_cast is permitted to strip cvr-qualifiers, only. Make sure that
- // the other qualifiers (e.g., address spaces) are identical.
- SrcQuals.removeCVRQualifiers();
- DestQuals.removeCVRQualifiers();
- if (SrcQuals != DestQuals)
- return TC_NotApplicable;
- }
-
- // Since we're dealing in canonical types, the remainder must be the same.
- if (SrcType != DestType)
+ // C++ [expr.const.cast]p3:
+ // "For two similar types T1 and T2, [...]"
+ //
+ // We only allow a const_cast to change cvr-qualifiers, not other kinds of
+ // type qualifiers. (Likewise, we ignore other changes when determining
+ // whether a cast casts away constness.)
+ if (!Self.Context.hasCvrSimilarType(SrcType, DestType))
return TC_NotApplicable;
if (NeedToMaterializeTemporary)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 6f01f19..35c3612 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -3087,7 +3087,7 @@
// in multi-level pointers, subject to the following rules: [...]
bool PreviousToQualsIncludeConst = true;
bool UnwrappedAnyPointer = false;
- while (Context.UnwrapSimilarPointerTypes(FromType, ToType)) {
+ while (Context.UnwrapSimilarTypes(FromType, ToType)) {
// Within each iteration of the loop, we check the qualifiers to
// determine if this still looks like a qualification
// conversion. Then, if all is well, we unwrap one more level of
@@ -3642,16 +3642,6 @@
return Result;
}
-static bool hasSimilarType(ASTContext &Context, QualType T1, QualType T2) {
- while (Context.UnwrapSimilarPointerTypes(T1, T2)) {
- Qualifiers Quals;
- T1 = Context.getUnqualifiedArrayType(T1, Quals);
- T2 = Context.getUnqualifiedArrayType(T2, Quals);
- }
-
- return Context.hasSameUnqualifiedType(T1, T2);
-}
-
// Per 13.3.3.2p3, compare the given standard conversion sequences to
// determine if one is a proper subset of the other.
static ImplicitConversionSequence::CompareKind
@@ -3675,7 +3665,7 @@
Result = ImplicitConversionSequence::Worse;
else
return ImplicitConversionSequence::Indistinguishable;
- } else if (!hasSimilarType(Context, SCS1.getToType(1), SCS2.getToType(1)))
+ } else if (!Context.hasSimilarType(SCS1.getToType(1), SCS2.getToType(1)))
return ImplicitConversionSequence::Indistinguishable;
if (SCS1.Third == SCS2.Third) {
@@ -3949,7 +3939,7 @@
: ImplicitConversionSequence::Better;
}
- while (S.Context.UnwrapSimilarPointerTypes(T1, T2)) {
+ while (S.Context.UnwrapSimilarTypes(T1, T2)) {
// Within each iteration of the loop, we check the qualifiers to
// determine if this still looks like a qualification
// conversion. Then, if all is well, we unwrap one more level of