summaryrefslogtreecommitdiff
path: root/lib/Sema/SemaCast.cpp
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2018-07-18 20:13:36 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2018-07-18 20:13:36 +0000
commit580f7daabc7696d50ad09d9643b2afeadbd387d8 (patch)
tree90caa25f363575e23215c43ece336ca7b88061d1 /lib/Sema/SemaCast.cpp
parent1a78aeb17c78bc38c5cc7580dccc49136079ac07 (diff)
downloadclang-580f7daabc7696d50ad09d9643b2afeadbd387d8.tar.gz
DR330: when determining whether a cast casts away constness, consider
qualifiers from all levels matching a multidimensional array. For example, this allows casting from pointer to array of array of const volatile int to pointer to const pointer to volatile pointer to int because the multidimensional array part of the source type corresponds to a part of the destination type that contains both 'const' and 'volatile'. Differential Revision: https://reviews.llvm.org/D49457 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@337422 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaCast.cpp')
-rw-r--r--lib/Sema/SemaCast.cpp95
1 files changed, 64 insertions, 31 deletions
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