-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang] Fix array types comparison in getCommonSugaredType #131649
Conversation
Const-qualification of an array caused by constexpr specifier can produce QualType, where the const qualifier is set both as fast qualifier and as a qualifier of the array element type. It can result in a compiler crash, because such QualType does not compare equal to the same type but without extra qualification. To avoid the crash, the redundant qualifiers are removed while searching for common sugar. It fixes llvm#97005 (Clang crashed in ASTContext::getCommonSugaredType).
@llvm/pr-subscribers-clang Author: Serge Pavlov (spavloff) ChangesConst-qualification of an array caused by constexpr specifier can produce QualType, where the const qualifier is set both as fast qualifier and as a qualifier of the array element type. It can result in a compiler crash, because such QualType does not compare equal to the same type but without extra qualification. To avoid the crash, the redundant qualifiers are removed while searching for common sugar. It fixes #97005 (Clang crashed in ASTContext::getCommonSugaredType). Full diff: https://github.com/llvm/llvm-project/pull/131649.diff 2 Files Affected:
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 7ed5b033d9bd8..2614c4235c994 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -14171,6 +14171,15 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X,
static auto unwrapSugar(SplitQualType &T, Qualifiers &QTotal) {
SmallVector<SplitQualType, 8> R;
while (true) {
+ if (const auto *ATy = dyn_cast<ArrayType>(T.Ty)) {
+ // C++ 9.3.3.4p3: Any type of the form "cv-qualifier-seq array of N U" is
+ // adjusted to "array of N cv-qualifier-seq U".
+ // C23 6.7.3p10: If the specification of an array type includes any type
+ // qualifiers, both the array and the element type are so-qualified.
+ //
+ // To simplify comparison remove the redundant qualifiers from the array.
+ T.Quals.removeCVRQualifiers(Qualifiers::Const | Qualifiers::Volatile);
+ }
QTotal.addConsistentQualifiers(T.Quals);
QualType NT = T.Ty->getLocallyUnqualifiedSingleStepDesugaredType();
if (NT == QualType(T.Ty, 0))
diff --git a/clang/test/SemaCXX/constexpr-implicit-const-97005.cpp b/clang/test/SemaCXX/constexpr-implicit-const-97005.cpp
new file mode 100644
index 0000000000000..db6106998d02b
--- /dev/null
+++ b/clang/test/SemaCXX/constexpr-implicit-const-97005.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s
+
+bool a;
+constexpr const unsigned char c[] = { 5 };
+constexpr const unsigned char d[1] = { 0 };
+auto b = (a ? d : c);
+
+constexpr const unsigned char c1[][1] = {{ 5 }};
+constexpr const unsigned char d1[1][1] = {{ 0 }};
+auto b1 = (a ? d1 : c1);
|
clang/lib/AST/ASTContext.cpp
Outdated
// qualifiers, both the array and the element type are so-qualified. | ||
// | ||
// To simplify comparison remove the redundant qualifiers from the array. | ||
T.Quals.removeCVRQualifiers(Qualifiers::Const | Qualifiers::Volatile); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tossing the const qualifier is fine if the array element type is already const-qualified... but do we actually know it is at this point? Or do we need something like T.Ty = getAsArrayType(getQualifiedType(T))
to make sure the qualifiers get preserved?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the updated version, only qualifiers found in element type are removed.
The test for this should actually go in One example test, without using constexpr: https://godbolt.org/z/8PGa4aTPx enum class Z {};
using ConstInt = const int;
using ConstIntArray1 = ConstInt[1];
using ConstIntArrayInc = ConstInt[];
const ConstIntArray1 c = { 5 };
const ConstIntArrayInc d = { 0 };
Z t1 = false ? d : c; Also I note there is a separate bug here in the type printer: https://godbolt.org/z/5afcxnor7 We get a diagnostic: About the fix, I think it's at the wrong level and we shouldn't just be dropping the qualifiers like that, but it's been a while I implemented this functionality and I would need some time to think about it. |
Regarding the double const , I think this is not a type printer bug per se. By indiscriminately adding a qualifier to a type, we can produce a type which cannot be written, such as this const qualified array case. As such, we can never do a good job printing these anyway. There are two issues:
|
clang/lib/AST/ASTContext.cpp
Outdated
if (const auto *ATy = dyn_cast<ArrayType>(T.Ty)) { | ||
// C++ 9.3.3.4p3: Any type of the form "cv-qualifier-seq array of N U" is | ||
// adjusted to "array of N cv-qualifier-seq U". | ||
// C23 6.7.3p10: If the specification of an array type includes any type | ||
// qualifiers, both the array and the element type are so-qualified. | ||
// | ||
// To simplify comparison remove the redundant qualifiers from the array. | ||
T.Quals.removeCVRQualifiers(Qualifiers::Const | Qualifiers::Volatile); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is at the wrong level to fix the problem.
We have the logic for dealing with Array qualifiers in getCommonArrayElementType
.
That's where we adjust the outer qualifiers for Array types accordingly to the qualifiers of the element type.
Looks like there is a bug in the qualifier algebra there:
QX += EX.getQualifiers() - RQ;
QY += EY.getQualifiers() - RQ;
We want to break down those two lines to:
QX += EX.getQualifiers();
QY += EY.getQualifiers();
QX -= RQ;
QY -= RQ;
I believe this should fix your problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change makes sense, it can fix te case of "array of arry", but it cannot fix the case from #97005:
bool a;
constexpr const unsigned char c[] = { 5 };
constexpr const unsigned char d[1] = { 0 };
auto b = (a ? d : c);
Here types of d
and d
do not contain sugar, corresponding canonical types are identical, getCommonNonSugarTypeNode
is not called and getCommonArrayElementType
is not called. However their types are not equal and assert(QX == QY)
fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, this needs more thought, but regardless, getCommonArrayElementType is the correct place to fix the problem.
We don't want to add special cases for ArrayTypes in unwrapSugar, because it's in the fast path, and most type nodes are not ArrayTypes, and most types don't even have ArrayTypes in them.
So this is why things are layered this way.
// RUN: %clang_cc1 -triple x86_64-linux-gnu %s | ||
|
||
bool a; | ||
constexpr const unsigned char c[] = { 5 }; | ||
constexpr const unsigned char d[1] = { 0 }; | ||
auto b = (a ? d : c); | ||
|
||
constexpr const unsigned char c1[][1] = {{ 5 }}; | ||
constexpr const unsigned char d1[1][1] = {{ 0 }}; | ||
auto b1 = (a ? d1 : c1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move this test to clang/test/SemaCXX/sugar-common-types.cpp
, adjusting according to the function and style of that file.
Ie we want to make sure the inner sugar is preserved in the output.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test has been moved.
- Now only qualifiers that are found in array element are cleared, - Test are moved into a new location.
You can test this locally with the following command:git-clang-format --diff 3f62718c4a907125af31faa62365bdf11ddef7b6 9bc13aee5775af148941826b74e5c9fbaab907c7 --extensions cpp -- clang/lib/AST/ASTContext.cpp clang/test/SemaCXX/sugar-common-types.cpp View the diff from clang-format here.diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 28f4332162..5429cdc704 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -14171,7 +14171,7 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X,
/// Returns element type of the array, probably multidimensional, specified by
/// the given ArrayType.
/// \returns Canonical type of the array element.
-static QualType getNonArrayElementType(const ArrayType* ATy) {
+static QualType getNonArrayElementType(const ArrayType *ATy) {
QualType ElTy = ATy->getElementType().getCanonicalType();
while (auto *SubArr = dyn_cast<ArrayType>(ElTy.getTypePtr()))
ElTy = SubArr->getElementType();
|
I posted an alternative fix for this here: #132559 |
Fixed in #132559. |
Const-qualification of an array caused by constexpr specifier can produce QualType, where the const qualifier is set both as fast qualifier and as a qualifier of the array element type. It can result in a compiler crash, because such QualType does not compare equal to the same type but without extra qualification.
To avoid the crash, the redundant qualifiers are removed while searching for common sugar.
It fixes #97005 (Clang crashed in ASTContext::getCommonSugaredType).