c++visual-c++language-lawyerconstexprc++23

Simple constexpr switch-case fails to compile on MSVC


If the order the case labels appears is swapped, it compiles fine (it seems to test the first branch or something even though its not called from anywhere) - Im curious about this behavior - is this a bug or feature?

The other major compilers does not seem to have this behavior. (they seem more 'lazy' and only actually tests the branches actually used at compile time)

Link to godbolt : https://godbolt.org/z/9eMeevq5K

Bonus question: what does the rules really for constexpr functions ?)

The following code gives compile error with MSVC:

#include <exception>    

enum Enum {
    a,
    b        
};    

constexpr int f (Enum val) {
    switch (val){
        case a: break;
        case b: return 43;
    }
    std::abort();
}

int main() {
    //return f(a);
    return 0;
}
Error:
<source>(13): error C3615: constexpr function 'f' cannot result in a constant expression
<source>(18): note: failure was caused by call of undefined function or one not declared 'constexpr'
<source>(18): note: see usage of 'abort'
<source>(13): note: failure was caused by control reaching the end of a constexpr function

at this line:

constexpr int f (Enum val) {

Solution

  • A constexpr function can also be evaluated at runtime, so it does not need to satisfy constexpr requirements on every execution path.

    The compiler checks whether the specific path taken during a constexpr function call meets constexpr requirements based on the given arguments. If it does, the computation is performed at compile time. If it does not, the computation occurs at runtime. If a non-constexpr result is used in a constexpr context, a compilation error occurs.

    Consider the following code:

    constexpr int f(int val) {
        if (val == 42) {
            std::abort();
        }
        else {
            std::abort();
        }
        return 42;
    }
    int main() {
        return f(1);
    }
    

    This code compiles successfully with MSVC /C++17, even though the function can never actually be a valid constexpr.

    After extensive testing, I suspect that MSVC requires a constexpr function to have at least one theoretically valid constexpr path, even if that path is never reachable.

    Regarding the OP’s issue, I believe this is a bug in the MSVC compiler.

    Consider the following code, tested with Visual Studio Community 2022 version 17.13.2 using /std:c++23preview (since goto is only allowed in constexpr starting from C++23):

    constexpr int f(int val) {
        switch (val) {
        case 42:
            std::abort();
            goto abor;
        default:
            return 43;
        }
        abor:
        std::abort();
    }
    

    This code compiles. However, the following code does not compile:

    constexpr int f(int val) {
        switch (val) {
        case 42:
            std::abort();
            break;
        default:
            return 43;
        }
        std::abort();
    }
    

    Even though both versions are logically equivalent, the break version fails to compile while the goto version succeeds. This is quite strange.

    Through further testing, I discovered that break and case seem to influence constexpr evaluation in an unexpected way. The exact reason is unclear—perhaps this is a bug in MSVC?

    Does anyone know how to report this issue?