Diagnose unreachable generic selection associations

The controlling expression of a _Generic selection expression undergoes
lvalue conversion, array conversion, and function conversion before
picking the association. This means that array types, function types,
and qualified types are all unreachable code if they're used as an
association. I've been caught by this twice in the past few months and
I figure that if a WG14 member can't seem to remember this rule, users
are also likely to struggle with it. So this adds an on-by-default
unreachable code diagnostic for generic selection expression
associations.

Note, we don't have to worry about function types as those are already
a constraint violation which generates an error.

Differential Revision: https://reviews.llvm.org/D125259
This commit is contained in:
Aaron Ballman 2022-05-10 11:14:24 -04:00
parent 71728360ad
commit ca75ac5f04
5 changed files with 41 additions and 1 deletions

View File

@ -210,6 +210,10 @@ Improvements to Clang's diagnostics
- ``-Wenum-conversion`` now warns on converting a signed enum of one type to an
unsigned enum of a different type (or vice versa) rather than
``-Wsign-conversion``.
- Added the ``-Wunreachable-code-generic-assoc`` diagnostic flag (grouped under
the ``-Wunreachable-code`` flag) which is enabled by default and warns the
user about ``_Generic`` selection associations which are unreachable because
the type specified is an array type or a qualified type.
Non-comprehensive list of changes in this release
-------------------------------------------------

View File

@ -843,9 +843,11 @@ def ReservedIdentifier : DiagGroup<"reserved-identifier",
//
def UnreachableCodeLoopIncrement : DiagGroup<"unreachable-code-loop-increment">;
def UnreachableCodeFallthrough : DiagGroup<"unreachable-code-fallthrough">;
def UnreachableCodeGenericAssoc : DiagGroup<"unreachable-code-generic-assoc">;
def UnreachableCode : DiagGroup<"unreachable-code",
[UnreachableCodeLoopIncrement,
UnreachableCodeFallthrough]>;
UnreachableCodeFallthrough,
UnreachableCodeGenericAssoc]>;
def UnreachableCodeBreak : DiagGroup<"unreachable-code-break">;
def UnreachableCodeReturn : DiagGroup<"unreachable-code-return">;
def UnreachableCodeAggressive : DiagGroup<"unreachable-code-aggressive",

View File

@ -693,6 +693,10 @@ def warn_unreachable_fallthrough_attr : Warning<
InGroup<UnreachableCodeFallthrough>, DefaultIgnore;
def note_unreachable_silence : Note<
"silence by adding parentheses to mark code as explicitly dead">;
def warn_unreachable_association : Warning<
"due to lvalue conversion of the controlling expression, association of type "
"%0 will never be selected because it is %select{of array type|qualified}1">,
InGroup<UnreachableCodeGenericAssoc>;
/// Built-in functions.
def ext_implicit_lib_function_decl : ExtWarn<

View File

@ -1685,6 +1685,25 @@ Sema::CreateGenericSelectionExpr(SourceLocation KeyLoc,
D = diag::err_assoc_type_nonobject;
else if (Types[i]->getType()->isVariablyModifiedType())
D = diag::err_assoc_type_variably_modified;
else {
// Because the controlling expression undergoes lvalue conversion,
// array conversion, and function conversion, an association which is
// of array type, function type, or is qualified can never be
// reached. We will warn about this so users are less surprised by
// the unreachable association. However, we don't have to handle
// function types; that's not an object type, so it's handled above.
unsigned Reason = 0;
QualType QT = Types[i]->getType();
if (QT->isArrayType())
Reason = 1;
else if (QT.hasQualifiers())
Reason = 2;
if (Reason)
Diag(Types[i]->getTypeLoc().getBeginLoc(),
diag::warn_unreachable_association)
<< QT << (Reason - 1);
}
if (D != 0) {
Diag(Types[i]->getTypeLoc().getBeginLoc(), D)

View File

@ -57,3 +57,14 @@ void GH50227(void) {
_Generic(n++, int : 0) // expected-error {{cannot increment value of type 'int ()'}} ext-warning {{'_Generic' is a C11 extension}}
), int : 0);
}
void unreachable_associations(const int i) {
_Static_assert( // ext-warning {{'_Static_assert' is a C11 extension}}
_Generic(i, // ext-warning {{'_Generic' is a C11 extension}}
const int : 1, // expected-warning {{due to lvalue conversion of the controlling expression, association of type 'const int' will never be selected because it is qualified}}
volatile int : 2, // expected-warning {{due to lvalue conversion of the controlling expression, association of type 'volatile int' will never be selected because it is qualified}}
int[12] : 3, // expected-warning {{due to lvalue conversion of the controlling expression, association of type 'int[12]' will never be selected because it is of array type}}
int : 4,
default : 5
) == 4, "we had better pick int!");
}