6

On GCC 7 I have enabled most of all warnings on Qt creator 4.9. Now I have a switch statement which covers all enumeration values. If I add a default: I get a warning (from Qt creator):

warning: default label in switch which covers all enumeration values

If I remove the default: I get another warning (from GCC):

error: this statement may fall through [-Werror=implicit-fallthrough=]  
       }  
       ^

error: all warnings being treated as errors

What am I supposed to do? Turn off warnings? They are useful, I don't want to turn off any of them, but Wimplicit-fallthrough seems to be faulty.

[[fallthrough]] doesn't help because the cases end with a return thus I get (from Qt creator):

warning: fallthrough annotation in unreachable code

__attribute__ ((fallthrough)) didn't do anything either. Neither did /* FALLTHRU */ or [[gnu::fallthrough]] or // fall through. Presumably because of -pedantic?

Example:

enum class E {a, b, c};

QVariant fun(E e) {
     switch (e) {
        case E::a: return "something";
        case E::b: return "something_else";
        case E::c: return "something_different";
        // default: return QVariant{};
        // Do I add a default:? What do I add here?
    }
}

Hopefully the things I've tried shows that my question is not a duplicate of this or this or other similar questions because they don't solve my problem.

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
  • What default did you remove? Could you show an example of your default? –  Aug 16 '19 at 23:15
  • @Chipster really, just any default. `QVariant{}` in my case but it doesn't matter. – Aykhan Hagverdili Aug 16 '19 at 23:16
  • Hmm, looks okay. Try adding a break after return, maybe? –  Aug 16 '19 at 23:17
  • @Chipster I get "warning: 'break' will never be executed" – Aykhan Hagverdili Aug 16 '19 at 23:18
  • Yikes. I'm surprised it doesn't like the return. Out of curiosity, why didn't [this question](https://stackoverflow.com/questions/44511436/how-to-do-an-explicit-fall-through-in-c)'s solution work for you? –  Aug 16 '19 at 23:19
  • 3
    @Ayxan You should post some actual compilable code. This snippet https://gcc.godbolt.org/z/qJaiVK based on your example compiles with gcc 7 without warning. – Petr Skocik Aug 16 '19 at 23:21
  • What is tye type of `some_var`? – MikeCAT Aug 16 '19 at 23:21
  • @Chipster I am guessing because of `-pedantic` I guess? And also I get "warning: declaration does not declare anything" – Aykhan Hagverdili Aug 16 '19 at 23:22
  • @PSkocik see the edit. My code compiles with Qt. – Aykhan Hagverdili Aug 16 '19 at 23:22
  • @MikeCAT see the edit. The type is `E`. – Aykhan Hagverdili Aug 16 '19 at 23:23
  • "Qt" isn't a compiler. If you are using QtCreator, that is an IDE which can have various compilers installed. Go into the kit configuration for your project to see which compiler and version is selected – M.M Aug 16 '19 at 23:25
  • 2
    @M.M as I mentioned in the question, it's GCC 7. I also wanted to note the IDE just in case it's helpful. – Aykhan Hagverdili Aug 16 '19 at 23:25
  • 2
    That's not very helpful as there are various builds of gcc 7. In any case you should post a [Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) showing all of the various conditions you are asking about – M.M Aug 16 '19 at 23:27
  • @M.M https://gcc.godbolt.org/z/yk7AqH – Aykhan Hagverdili Aug 16 '19 at 23:28
  • 4
    @Ayxan in that code the errors are because you specified `-ansi` flag, (which means `-std=c++98`) , and you used syntax that is invalid in C++98, `enum class`. Remove that flag and there are no errors and no warnings – M.M Aug 16 '19 at 23:30
  • @M.M correct. Looks like the IDE is adding its own warnings. I'll add this to the question – Aykhan Hagverdili Aug 16 '19 at 23:32
  • If you're talking about the messages that appear inline in QtCreator (and in the Issues tab in real time, not when you build), those come from the Clang Code Model. I'm not entirely sure but I think it uses whatever `clang` resolves to in the shell where you launched the IDE (as opposed to configuring compiler paths and options and so on). There's a section of the IDE where you can configure which flags are sent to clang for this, maybe the solution will be either to upgrade clang, or change those flags – M.M Aug 16 '19 at 23:41
  • Also there might be warnings from clang-tidy – M.M Aug 16 '19 at 23:44
  • @M.M I think you are right, and some of the warnings are from Clang Code. The accepted answer by [Miles Budnek](https://stackoverflow.com/users/4151599/miles-budnek) solves the issue with a great explanation. – Aykhan Hagverdili Aug 16 '19 at 23:49

5 Answers5

9

Consider fun(static_cast<E>(42)). This is a perfectly well-defined conversion, but the function from your question will reach the end without returning, and your program's behavior will be undefined. That's why GCC warns that control may reach the end of a non-void function.

So why not add a default case? Consider what happens if someone goes back and adds another constant to E, but forgets to update fun. Without a default, GCC will helpfully warn you that the switch doesn't handle all of E's constants. If you add a default case, you've defeated that very helpful protection.

So what's the Right ThingTM to do? Return your default value (or throw or call abort() to crash, as appropriate) at the end of the function, after the switch:

enum class E {a, b, c};

QVariant fun(E e) {
     switch (e) {
        case E::a: return "something";
        case E::b: return "something_else";
        case E::c: return "something_different";
    }
    return "some_default"; // or throw or abort()
}

This gives you the best of both worlds. If someone passes a value that isn't one of the pre-defined enumerator constants, it will behave in a well-defined way. If someone adds a new constant to E and forgets to update fun then the compiler will still issue a warning.

For further discussion, Jason Turner covered this topic (among others) in his CppCon 2018 talk, which is worth a watch.

Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
  • Depending on where `e` comes from, a different option could be to choose to fail well and abort if `e` is an unknown value. Death by user input is a damn stupid way to go, so handle the bad input and continue, but if the program itself came up with and provided some insane value for `e`, there's a bug somewhere and who knows what else in the program's been corrupted. I see that as a good time to die. – user4581301 Aug 17 '19 at 00:35
  • It's 2020 and GCC 10 now thinks your version requires a warning about missing `default` too. – Aykhan Hagverdili Aug 11 '20 at 10:51
2

If my first answer doesn't satisfy, this perhaps this will. This is how I resolved the issue locally:

QVariant fun(E e) {
    switch (e) {
        case a: return "something";
        case b: return "something_else";
        case c: return "something_different";
    }
    return "";
}
ManuelSchneid3r
  • 15,850
  • 12
  • 65
  • 103
selbie
  • 100,020
  • 15
  • 103
  • 173
  • This does solve the problem (`+1`) but I'd like to know how you decided to do this. – Aykhan Hagverdili Aug 17 '19 at 05:32
  • What Miles said - you are missing a final return statement for the (hypothetical) scenario of when the `switch` statement falls through without returning. – selbie Aug 17 '19 at 06:14
1

The compiler is clearly confused with different warnings being enabled and the inline return statements. Just make it happy.

enum class E {a, b, c};

QVariant fun(E e) {
     const char* result = "";
     switch (e) {
        case E::a: {
            result = "something"; 
            break;
        }
        case E::b: {
             result = "something_else";
             break;
        }
        case E::c: {
            result = "something_different"; 
            break;
        }
    }
    return result;
}
selbie
  • 100,020
  • 15
  • 103
  • 173
  • Umm, that won't compile, will it? `result` is const. –  Aug 16 '19 at 23:30
  • 1
    @Chipster `const char *result` is a non-const pointer to const `char`. – HolyBlackCat Aug 16 '19 at 23:30
  • @HolyBlackCat You're right. I just looked it up to refresh my memory on const * vs * const. Nvm, then. –  Aug 16 '19 at 23:32
  • 2
    Rather than `result` being of type `const char *`, why not make it `QVariant`? That's what the function returns. – Peter Aug 16 '19 at 23:52
1

If this all there is, why go to the trouble of a function?

Is the goal simply to mate the key of E::a to "something" and E::b to "something-else" etc.. ?

Are the results preset at compile time? The enum from the OP appears to indicate that the keys are known at compile time.

A simple array would suffice. Unless I'm missing something, nothing really gained by the switch

jdw
  • 76
  • 8
1

no it may not

Certainly it can.

Believe your compiler. It's smarter than you!

An enumeration is not an exhaustive list of all possible values. It is a set of names for some values of the domain.

Casts are not only possible here; they are to be expected if you wish to provide a stable and robust interface.

So, you either need to provide a default, or tell the compiler via an attribute or intrinsic that you know (based on domain knowledge that it cannot guess) that you know no other possibility will occur.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • *"Believe your compiler"* Good answer (`+1`) but it's not like I chose not to believe my compiler. It's just the compiler and Clang Code gave me contradictory warnings, one asking to add `default:` one asking to remove it. The accepted answer solves both of the warnings. – Aykhan Hagverdili Aug 17 '19 at 05:28