GCC 7, -Wimplicit-fallthrough warnings, and portable way to clear them?
Asked Answered
C

6

77

We are catching warnings from GCC 7 for implicit fall through in a switch statement. Previously, we cleared them under Clang (that's the reason for the comment seen below):

g++ -DNDEBUG -g2 -O3 -std=c++17 -Wall -Wextra -fPIC -c authenc.cpp
asn.cpp: In member function ‘void EncodedObjectFilter::Put(const byte*, size_t)’:
asn.cpp:359:18: warning: this statement may fall through [-Wimplicit-fallthrough=]
    m_state = BODY;  // fall through
                  ^
asn.cpp:361:3: note: here
   case BODY:
   ^~~~

The GCC manual states to use __attribute__ ((fallthrough)), but its not portable. The manual also states "... it is also possible to add a fallthrough comment to silence the warning", but it only offer FALLTHRU (is this really the only choice?):

switch (cond)
  {
  case 1:
    bar (0);
    /* FALLTHRU */
  default:
    …
  }

Is there a portable way to clear the fall through warning for both Clang and GCC? If so, then what is it?

Clyde answered 16/7, 2017 at 14:35 Comment(7)
As you are using C++17 see attribute [[fallthrough]]; (note - needs the ;) source: en.cppreference.com/w/cpp/language/attributesPurvey
Thanks Richard. Unfortunately, NO, we are not attributes at the moment. We are still supporting back to C++03. -std=c++17 is just testing one configuration under Fedora 26 because it supplies GCC 7. But I would entertain a macro to abstract it for portability.Clyde
Maybe BOOST_FALLTHROUGH does what you need. boost.org/doc/libs/master/libs/config/doc/html/boost_config/…Aeneous
Thanks @BaummitAugen - Yeah, that could probably work. Would you happen to know if __has_feature(x) works for this? Is it enough to test with __has_feature(fallthrough)? Or do we need to do this with compiler version tests? Or maybe something else?Clyde
As far as I understood the docs, you just put BOOST_FALLTHROUGH; in your code and then it just expands to the right thing to cancel the warning. If the compiler in use warns, but does not have a way to silence the warning, there's nothing we can do anyways I guess.Aeneous
@BaummitAugen - My bad, we don't use Boost. I've got to build an equivalent macro from scratch. That's what the additional questions were about.Clyde
I would guess this will require some compiler version tests, but I'm not sure.Aeneous
M
96

GCC expects the marker comment on its own line, like this:

  m_state = BODY;
  // fall through
case BODY:

The marker also has to come right before the case label; there cannot be an intervening closing brace }.

fall through is among the markers recognized by GCC. It's not just FALLTHRU. For a full list, see the documentation of the -Wimplicit-fallthrough option. Also see this posting on the Red Hat Developer blog.

C++17 adds a [[fallthrough]] attribute that can be used to suppress such warnings. Note the trailing semicolon:

  m_state = BODY;
  [[fallthrough]];
case BODY:

Clang supports -Wimplicit-fallthrough warnings, but does not enable them as part of -Wall or -Wextra. Clang does not recognize comment markers, so the attribute-based suppression has to be used for it (which currently means the non-standard __attribute__((fallthrough)) construct for the C front end).

Note that suppressing the warning with marker comments only works if the compiler actually sees the comment. If the preprocessor runs separately, it needs to be instructed to preserve comments, as with the -C option of GCC. For example, to avoid spurious warnings with ccache, you need to specify the -C flag when compiling, or, with recent versions of ccache, use the keep_comments_cpp option.

Mikamikado answered 17/7, 2017 at 6:36 Comment(10)
Thanks Florian. I tried GCC 7 on Fedora 26 (gcc (GCC) 7.1.1 20170622 (Red Hat 7.1.1-3)). Moving // fall through to the bottom as shown in your example still produced the warning. Here is the real source file in case its needed: asn.cpp. If it matters, I am using -std=c++17.Clyde
Where exactly did you place the comment? Before or after the }? You need to place it on the line immediately before the case keyword.Mikamikado
Thanks again Florian. Yep, you're right... The // fall through was inside the closing brace. I just realized it myself, and came back to accept your answer after testing things the way they were intended. Sorry about that.Clyde
Thanks again Florian. It appears others are having some issues with GCC's implementation. See -Wimplicit-fallthrough broken? on the GCC Help list.Clyde
FYI: -save-temps option breaks the comment recognition heuristic. The warning is not suppressed in that caseDivisionism
Although fallthrough comments may be portable, comments in code are designed to not live through the preprocessor, and with a tool like ccache, that makes use of the preprocessor output, any such comments will be removed before the preprocessed output is sent to gcc for compilation. Hence, if you are using ccache, you will still see these warnings.Rodolforodolph
@BlueDemon, try ccache with keep_comments_cpp set to true. Does it help?Mikamikado
@Florian Weimer, keep_comments_cpp is a ccache 3.3 feature and since I'm using ccache 3.2.7 I don't know (yet). On monday, I will test it though. Thanks for the idea!Rodolforodolph
Thanks, I must have botched my initial testing. Answer updated.Mikamikado
This does not work on gcc 9 -- a // fall through comment on a line by itself immediately before the next case does not supress the warning.Mera
A
18

C++17 [[fallthrough]]

Example:

int main(int argc, char **argv) {
    switch (argc) {
        case 0:
            argc = 1;
            [[fallthrough]];
        case 1:
            argc = 2;
    };
}

Compile with:

g++ -std=c++17 -Wimplicit-fallthrough main.cpp

If you remove the [[fallthrough]];, GCC warns:

main.cpp: In function ‘int main()’:
main.cpp:5:15: warning: this statement may fall through [-Wimplicit-fallthrough=]
             argc = 1;
             ~~^~~
main.cpp:6:9: note: here
         case 1:
         ^~~~

Also note from the example that the warning only happens if you fall beacross two cases: the last case statement (case 1 here) generates no warnings even though it has no break.

The following constructs don't generate the warning either:

#include <cstdlib>

[[noreturn]] void my_noreturn_func() {
    exit(1);
}

int main(int argc, char **argv) {
    // Erm, an actual break
    switch (argc) {
        case 0:
            argc = 1;
            break;
        case 1:
            argc = 2;
    }

    // Return also works.
    switch (argc) {
        case 0:
            argc = 1;
            return 0;
        case 1:
            argc = 2;
    }

    // noreturn functions are also work.
    // https://mcmap.net/q/116342/-what-is-the-point-of-noreturn/47444782#47444782
    switch (argc) {
        case 0:
            argc = 1;
            my_noreturn_func();
        case 1:
            argc = 2;
    }

    // Empty case synonyms are fine.
    switch (argc) {
        case 0:
        case 1:
            argc = 2;
    }

    // Magic comment mentioned at:
    // https://mcmap.net/q/119128/-gcc-7-wimplicit-fallthrough-warnings-and-portable-way-to-clear-them
    switch (argc) {
        case 0:
            argc = 1;
            // fall through
        case 1:
            argc = 2;
    }

    switch (argc) {
        // GCC extension for pre C++17.
        case 0:
            argc = 1;
            __attribute__ ((fallthrough));
        case 1:
            argc = 2;
    }

    switch (argc) {
        // GCC examines all braches.
        case 0:
            if (argv[0][0] == 'm') {
                [[fallthrough]];
            } else {
                return 0;
            }
        case 1:
            argc = 2;
    }
}

We can see from the last one that GCC examines all possible branches, and warns if any of them don't have [[fallthrough]]; or break or return.

You might also want to check for feature availability with macros as in this GEM5 inspired snippet:

#if defined __has_cpp_attribute
    #if __has_cpp_attribute(fallthrough)
        #define MY_FALLTHROUGH [[fallthrough]]
    #else
        #define MY_FALLTHROUGH
    #endif
#else
    #define MY_FALLTHROUGH
#endif

See also: https://en.cppreference.com/w/cpp/language/attributes/fallthrough

Tested on GCC 7.4.0, Ubuntu 18.04.

See also

C version of this question: How to do an explicit fall-through in C

Afroamerican answered 8/10, 2018 at 17:23 Comment(0)
P
3

Clean C solution:

int r(int a) {
    switch(a) {
    case 0:
        a += 3;
    case 1:
        a += 2;
    default:
        a += a;
    }
    return a;
}

becomes:

int h(int a) {
    switch(a) {
    case 0:
        a += 3;
        goto one;
    case 1:
    one:
        a += 2;
        goto others;
    default:
    others:
        a += a;
    }
    return a;
}

EDIT: Moved the labels after case statements, as suggested by Stéphane Gourichon in comments, to see the fallthrough more easily.

Peremptory answered 7/8, 2019 at 7:37 Comment(1)
I like the idea because it is fully portable. Is it really effective to put the extra label before the case ? Technically there is still a fallthrough. I would consider instead things like : goto one then case 1: then one: in that order. Gcc accepts both variants.Anecdotic
M
3

Another example: The Linux kernel provides a fallthrough pseudo-keyword macro. Which can be used as:

switch (cond) {
case 1:
    foo();
    fallthrough;
case 2:
    bar();
    break;
default:
    baz();
}

In kernel v5.10 its implemented like this:

#if __has_attribute(__fallthrough__)
# define fallthrough                    __attribute__((__fallthrough__))
#else
# define fallthrough                    do {} while (0)  /* fallthrough */
#endif
Macaronic answered 22/12, 2020 at 10:47 Comment(0)
A
2

Nobody mentioned disabling the warning altogether and this might not be the answer the OP was looking for but I think it should be included for completeness since it also works for both compilers:

-Wno-implicit-fallthrough

If for some reason you are not able to change the source code, this keeps the compile output clean allowing for a clear view on other problems (but of course one has to be aware of what one loses).

Antisepticize answered 1/5, 2021 at 21:49 Comment(1)
If there is one thing that is more annoying than no oversight then it is overreaching oversight enabled by a bad rule set. Compiler warnings are one such example.Antisepticize
S
0

You can also try:

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
// your code
#pragma GCC diagnostic pop
Sauerkraut answered 15/6, 2022 at 9:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.