Bug in Clang-12? "case value is not a constant expression"
Asked Answered
P

1

12

I stumbled upon a strange compile error in Clang-12. The code below compiles just fine in GCC 9. Is this a bug in the compiler or is there an actual problem with my code and GCC is just too forgiving?

#include<atomic>

enum X {
        A
};

class U {
        public:
        std::atomic<X> x;
        U() { x = A; }
};

template<typename T>
class V : public U {
        public:
        V() {
                switch(x) {
                        case A:
                        break;
                }
        }
};

int main() {
        V<void> v;
        return 0;
}

The code also compiles fine in Clang-12 if I remove the template<typename T> line and just write V v; instead of V<void> v;. The problem also goes away if I make x non-atomic. My compile command is:

clang++-12 test.cpp -std=c++17 -o test

I get the following compiler output:

test.cpp:18:9: error: case value is not a constant expression
                        case A:
                             ^
test.cpp:17:10: warning: enumeration value 'A' not handled in switch [-Wswitch]
                switch(x) {
                       ^
test.cpp:25:10: note: in instantiation of member function 'V<void>::V' requested here
        V<void> v;
                ^
1 warning and 1 error generated.
Plainclothesman answered 18/3, 2022 at 22:9 Comment(3)
It also compiles if you use this->x. Interesting pair of diagnostics at any rate.Meany
I'm sure I've seen something very similar posted recently. Can't find it though. Something to do with the depths of how class templates handle embedded template-based members, or some such ...Angola
I am voting to reopen the question because I realized now that the code in the linked question works fine since Clang 10, while the one here doesn't.Sardinia
S
5

Looks like a bug to me. Modifying case A to case nullptr gives the following error message (on the template definition):

error: no viable conversion from 'std::nullptr_t' to 'std::atomic<X>'

Making the class template into a class as suggested in the question then gives

error: value of type 'std::nullptr_t' is not implicitly convertible to 'int'

The latter message is correct. When class types are used in a switch condition they should be contextually implicitly converted to a integral or enumeration type and then be promoted. Here it should use std::atomic<X>'s conversion function which converts to X and is then promoted to int.

It shouldn't matter whether the statement appears in a template or not.

The base class is not dependent, so referring to the member x directly without this-> is also fine.

A conversion of A to std::atomic<X> would not be a constant expression, which is probably where the diagnostic comes from.

Here is an open Clang bug report and here is another potentially related bug report.

Actually, although the two mentioned bug reports are still open, the test code they present seems to work correctly since Clang 10. It seems to me that the variant in your question using a base class is a special case missed by that fix.

Sardinia answered 18/3, 2022 at 22:58 Comment(2)
class types can be used but they need an implicit conversion to an integral or enumeration type eel.is/c++draft/stmt.select#stmt.switch-2 . atomic<T> is implicitly convertible to T, it should behave like load( ) with default arguments.Washerwoman
@Washerwoman That's what I meant to say, maybe it wasn't worded well.Sardinia

© 2022 - 2024 — McMap. All rights reserved.