-
Notifications
You must be signed in to change notification settings - Fork 10.8k
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][Sema] Fix type of enumerators in incomplete enumerations #84068
Conversation
@llvm/pr-subscribers-clang Author: None (Kupa-Martin) ChangesEnumerators dont have the type of their enumeration before the closing brace. In these cases Expr::getEnumCoercedType() incorrectly returned the enumeration type. Introduced in PR #81418 Full diff: https://github.com/llvm/llvm-project/pull/84068.diff 2 Files Affected:
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index b4de2155adcebd..9a28e6f750069b 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -264,10 +264,13 @@ namespace {
}
QualType Expr::getEnumCoercedType(const ASTContext &Ctx) const {
- if (isa<EnumType>(this->getType()))
+ if (isa<EnumType>(this->getType())) {
return this->getType();
- else if (const auto *ECD = this->getEnumConstantDecl())
- return Ctx.getTypeDeclType(cast<EnumDecl>(ECD->getDeclContext()));
+ } else if (const auto *ECD = this->getEnumConstantDecl()) {
+ const auto *ED = cast<EnumDecl>(ECD->getDeclContext());
+ if (ED->isCompleteDefinition())
+ return Ctx.getTypeDeclType(ED);
+ }
return this->getType();
}
diff --git a/clang/test/Sema/warn-compare-enum-types-mismatch.c b/clang/test/Sema/warn-compare-enum-types-mismatch.c
index 2b72aae16b977a..1094a6972e778d 100644
--- a/clang/test/Sema/warn-compare-enum-types-mismatch.c
+++ b/clang/test/Sema/warn-compare-enum-types-mismatch.c
@@ -6,7 +6,9 @@ typedef enum EnumA {
} EnumA;
enum EnumB {
- B
+ B,
+ B1 = 1,
+ B2 = A == B1
};
enum {
|
@erichkeane I cant add the previous reviewers myself, could you do it for me please? |
Also, this probably needs a release note. |
If you want I'll add one but this bug has been on main no longer than a week, so I didnt think it would be necessary. |
bc9f937
to
92c0ebc
Compare
I see. Yeah, I don’t think we really need one if the bug was introduced and fixed in the same version. |
Thats correct, a release note is only necessary in cases where the bug existed in a release. |
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.
Should we also have a C++ test for this fix?
clang/test/Sema/warn-compare-enum-types-mismatch.c should cover both C and C++. Or do you mean some other kind of test? |
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.
Hmm, the rules are different between C and C++, but I think we may be okay. In C++:
https://eel.is/c++draft/enum#dcl.enum-5.sentence-6
Following the closing brace of an enum-specifier, each enumerator has the type of its enumeration.
In C23 6.7.3.3p12:
During the processing of each enumeration constant in the enumerator list, the type of the enumeration constant shall be:
p15:
The enumeration member type for an enumerated type without fixed underlying type upon completion is:
— int if all the values of the enumeration are representable as an int; or,
— the enumerated type.140)
Footnote 140) The integer type selected during processing of the enumerator list (before completion) of the enumeration may not be the same as the compatible implementation-defined integer type selected for the completed enumeration.
So I believe:
typedef enum EnumA {
A
} EnumA;
enum EnumB {
B,
B1 = 1,
B2 = A == B1
};
is not an enum compare warning in C++ because B1
doesn't have an enumeration type due to the enumeration not being fully-defined, and is not an enum compare warning in C because A
has type int
(due to p15) and B1
has type int
(due to p12).
I think we play a bit fast-and-loose with the way we treat enum constants in C in Clang, but that's outside of the scope of this patch. So LGTM aside from some tiny nits!
clang/lib/AST/Expr.cpp
Outdated
if (isa<EnumType>(getType())) { | ||
return getType(); | ||
} else if (const auto *ECD = getEnumConstantDecl()) { | ||
const auto *ED = cast<EnumDecl>(ECD->getDeclContext()); | ||
if (ED->isCompleteDefinition()) | ||
return Ctx.getTypeDeclType(ED); | ||
} |
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.
if (isa<EnumType>(getType())) { | |
return getType(); | |
} else if (const auto *ECD = getEnumConstantDecl()) { | |
const auto *ED = cast<EnumDecl>(ECD->getDeclContext()); | |
if (ED->isCompleteDefinition()) | |
return Ctx.getTypeDeclType(ED); | |
} | |
if (isa<EnumType>(getType())) | |
return getType(); | |
if (const auto *ECD = getEnumConstantDecl()) { | |
const auto *ED = cast<EnumDecl>(ECD->getDeclContext()); | |
if (ED->isCompleteDefinition()) | |
return Ctx.getTypeDeclType(ED); | |
} |
No else
after return
via our coding standard, NFC
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.
done
@@ -6,7 +6,9 @@ typedef enum EnumA { | |||
} EnumA; | |||
|
|||
enum EnumB { | |||
B | |||
B, |
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.
It might be good to capture some of what I wrote in the review summary as a comment here so it's clear why this is correct in both C and C++.
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.
done
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.
I think what I was asking, was do we have an equivalent C++ test that verifies in a .cpp
file that we also do not obtain a diagnostic for this.
Yes, you are correct.
There are enough differences between the C23 and C++ standards to produce different diagnostics. I've made a godbolt snippet that tries to keep track of the type of different enumerators in C23 and C++; we warn when we shouldnt and we dont warn when we should. In any case, this patch didnt intend to cover C23. |
92c0ebc
to
4bbd5cf
Compare
Right, so do we have a test for C++ that verifies that. |
@shafik We dont have a dedicated cpp test for this. I can add one if you want, but clang/test/Sema/warn-compare-enum-types-mismatch.c runs clang both on C and C++ mode, so I didnt think it necessary. |
I think we just a bug that demonstrates this issue: #84712 So if this fixes that as well then we should add tests for those cases too. |
Regarding #84712: From what I can tell, the behaviour of cases such as enum e1 { a };
enum e2 { b = a }; has changed between C++11 and C++14 (at least the wording is different starting with C++14; there may be some other section that states the same for C++11, but I couldn’t find one); specifically, C++11’s [dcl.enum]p5 states that:
Whereas in C++14 (and later), that same section reads:
That is, not only is the type of |
4bbd5cf
to
57538a2
Compare
I've included a separate test for C++. If no further changes are needed, could someone please merge this for me? I dont have commit access. |
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.
Aside from some formatting issues, this LGTM. Once you push up a fix for the formatting, I'll land. Thank you!
@@ -1,12 +1,19 @@ | |||
// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wenum-compare -Wno-unused-comparison %s | |||
// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wenum-compare -Wno-unused-comparison %s | |||
|
|||
// In C enumerators (i.e enumeration constants) have type int (until C23). In order to support diagnostics such as |
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.
There's trailing whitespace here and it goes over the 80-col limit; can you reformat?
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.
done
B | ||
B, | ||
B1 = 1, | ||
// In C++ this comparison doesnt warn as enumerators dont have the type of their enumeration before the closing |
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.
Same issue here as above with trailing whitespace and 80 col.
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.
done
Enumerators dont have the type of their enumeration before the closing brace. In these cases Expr::getEnumCoercedType() incorrectly returned the enumeration type. Introduced in PR llvm#81418 Fixes llvm#84712
57538a2
to
cc6a132
Compare
Enumerators dont have the type of their enumeration before the closing brace. In these cases Expr::getEnumCoercedType() incorrectly returned the enumeration type.
Introduced in PR #81418
Fixes #84712