Bug 61864

Summary: -Wcovered-switch-default to identify "dead" default branch
Product: gcc Reporter: Chengnian Sun <chengniansun>
Component: cAssignee: Not yet assigned to anyone <unassigned>
Status: NEW ---    
Severity: enhancement CC: dimhen, egallager, manu, mpolacek, rafael, sjames
Priority: P3 Keywords: diagnostic
Version: 5.0   
Target Milestone: ---   
See Also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67314
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87951
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87950
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105273
Host: Target:
Build: Known to work:
Known to fail: Last reconfirmed: 2014-07-22 00:00:00
Bug Depends on:    
Bug Blocks: 87403    
Attachments: test case demonstrating different switch-related warnings

Description Chengnian Sun 2014-07-21 05:30:15 UTC
Currently, GCC supports -Wswitch and -Wswitch-enum. But it does not warn on the case that all the possible values of an enum are covered in a switch, making the default branch unreachable. 

The following code is extracted and simplified from the SPEC benchmark, the enum only has three values, which are all covered in the switch. Therefore the default branch is "dead". 
 

$: cat t.c
enum clust_strategy {
  CLUSTER_MEAN, CLUSTER_MAX, CLUSTER_MIN
};

int Cluster(enum clust_strategy mode) {
  switch(mode) {
  case CLUSTER_MEAN: break;
  case CLUSTER_MAX: break;
  case CLUSTER_MIN: break;
  default: break;
  }
  return 0;
}
$: clang-trunk -Wcovered-switch-default -c t.c
t.c:10:3: warning: default label in switch which covers all enumeration values [-Wcovered-switch-default]
  default: break;
  ^
1 warning generated.
$:
Comment 1 Marek Polacek 2014-07-22 15:33:35 UTC
Confirmed.
Comment 2 Eric Gallager 2014-12-22 02:26:21 UTC
this(In reply to Chengnian Sun from comment #0)
> Currently, GCC supports -Wswitch and -Wswitch-enum.

It also supports "-Wswitch-default", which seems to be the exact opposite of clang's "-Wcovered-switch-default". Personally I think gcc's "-Wswitch-default" is the better one to listen to; having a default case is a good back-up even if it will never actually be reached. Here is a link to a post that explains it better than I could: http://programmers.stackexchange.com/a/242896/161051
Comment 3 Jakub Jelinek 2015-04-22 11:57:52 UTC
GCC 5.1 has been released.
Comment 4 Eric Gallager 2015-07-06 00:22:57 UTC
Actually, after giving this some more thought, and reading the GCC documentation some more, I came up with some ideas for a compromise that could allow both -Wswitch-default and -Wcovered-switch-default to be used without conflicting:

* Seeing as -Wcovered-switch-default (as clang implements it) makes certain assumptions about the nature of enums, its implementation in GCC should be restricted to only being active when the -fstrict-enums flag is also in use. As -fstrict-enums is a C++-only flag, this would effectively make -Wcovered-switch-default a C++-only warning, which would allow C-only programmers (such as myself) to avoid the flag.
* Besides general philosophical issues with the -Wcovered-switch-default flag, there's also two specific implementation issues I have with clang's implementation of it that I would want to see GCC fix in its own implementation. I wrote a quick source file to demonstrate:

$ cat switches.c
#include <stdlib.h> /* for rand() */
int main(void)
{
    int swindex0 = rand();
    enum values {
        FIRST_ENUMERATOR,
        SECOND_ENUMERATOR,
        THIRD_ENUMERATOR
    } swindex1 = SECOND_ENUMERATOR; /* silence unitialized warnings */
    /* do the non-enumerator switch first: */
    switch (swindex0) {
        case 1:
            swindex1 = FIRST_ENUMERATOR;
            break;
        case 2:
            swindex1 = SECOND_ENUMERATOR;
            break;
        case 3:
            swindex1 = THIRD_ENUMERATOR;
            break;
        /* -Wswitch-default: usual warning about switch with no default */
    }
    if (swindex0 > 3) {
        swindex1 = THIRD_ENUMERATOR;
    } else if (swindex0 < 1) {
        swindex1 = FIRST_ENUMERATOR;
    }

    switch (swindex1) {
        case FIRST_ENUMERATOR:
            break;
        case SECOND_ENUMERATOR:
            break;
        case THIRD_ENUMERATOR:
            break;
        /* -Wcovered-switch-default: print a warning here like clang's,
         * but only if also used with -fstrict-enums: */
        default:
            break;
    }
    switch (swindex1) {
        case FIRST_ENUMERATOR:
            break;
        case SECOND_ENUMERATOR:
            break;
        case THIRD_ENUMERATOR:
            break;
        /* -Wswitch-default: still warns:
         * * if by itself, the usual message, as in the first switch.
         * * if with -Wcovered-switch-default, use a different message,
         *   like:
         *   "Warning: switch missing default case which would be covered if it had one"
         *   "Note: use -fstrict-enums to silence this warning"
         * * if with -fstrict-enums, do like mentioned in the previous,
         *   and print no warning. */
    }
    switch (swindex1) {
        case FIRST_ENUMERATOR:
            break;
        case SECOND_ENUMERATOR:
            break;
        case THIRD_ENUMERATOR:
            break;
        /* Ideally there should be no warning here whatsoever; clang still
         * warns about it though: */
        default: __builtin_unreachable(); /*NOTREACHED*/
    }
    switch (swindex1) {
        case FIRST_ENUMERATOR:
            break;
        case SECOND_ENUMERATOR:
            break;
        case THIRD_ENUMERATOR: /*FALLTHROUGH*/
        /* Ideally there should be no warning from -Wcovered-switch-default
         * here, because even though there are labels for all enum values,
         * the 3rd case still falls through to the default case: */
        default: /*REACHEDBYFALLTHROUGH*/
            break;
        /* (clang still warns here though) */
    }
}

$ clang --version && clang -c -Weverything switches.c
clang version 3.6.0 (tags/RELEASE_360/final)
Target: x86_64-apple-darwin10.8.0
Thread model: posix
switches.c:38:9: warning: default label in switch which covers all enumeration values [-Wcovered-switch-default]
        default:
        ^
switches.c:66:9: warning: default label in switch which covers all enumeration values [-Wcovered-switch-default]
        default: __builtin_unreachable(); /*NOTREACHED*/
        ^
switches.c:77:9: warning: default label in switch which covers all enumeration values [-Wcovered-switch-default]
        default: /*REACHEDBYFALLTHROUGH*/
        ^
3 warnings generated.


$ g++-6_git --version && g++-6_git -c -fstrict-enums -Wall -Wextra -Wswitch -Wswitch-default -Wswitch-enum switches.c
g++-6_git (GCC) 6.0.0 20150604 (experimental)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

switches.c: In function 'int main()':
switches.c:11:12: warning: switch missing default case [-Wswitch-default]
     switch (swindex0) {
            ^
switches.c:41:12: warning: switch missing default case [-Wswitch-default]
     switch (swindex1) {
            ^

I'll add the file as an attachment, too.
Comment 5 Eric Gallager 2015-07-06 00:24:28 UTC
Created attachment 35915 [details]
test case demonstrating different switch-related warnings
Comment 6 Richard Biener 2015-07-16 09:12:41 UTC
GCC 5.2 is being released, adjusting target milestone to 5.3.
Comment 7 Richard Biener 2015-12-04 10:45:56 UTC
GCC 5.3 is being released, adjusting target milestone.
Comment 8 Manuel López-Ibáñez 2016-02-15 23:21:01 UTC
Non-regressions don't have a milestone.