Bug 71534 - [C++11/14] Initializing a static constexpr data member of a base class by using a static constexpr data member of a derived class should be an error
Summary: [C++11/14] Initializing a static constexpr data member of a base class by usi...
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 6.1.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: accepts-invalid
Depends on:
Blocks: constexpr
  Show dependency treegraph
 
Reported: 2016-06-14 17:41 UTC by Michele Caini
Modified: 2021-07-23 02:44 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2021-07-23 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Michele Caini 2016-06-14 17:41:01 UTC
Consider the following code:

    template<typename T>
    struct S { static constexpr int bar = T::foo; };

    struct U: S<U> { static constexpr int foo = 42; };

    int main() { }

GCC compiles it, but it shouldn't.
The type T is not a complete type when bar is initialized.

Here is a discussion on stackoverflow where reasons are discussed: http://stackoverflow.com/questions/37816186/initializing-a-static-constexpr-data-member-of-the-base-class-by-using-a-static

Link to the standard (working draft) are provided as well in the discussion.

Other compilers (mostly clang) correctly reject it.
Comment 1 Martin Sebor 2016-06-15 18:59:29 UTC
We have been interpreting the relevant text in temp.inst, p1 that:

"The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, [...] static data members [...]; and it causes the implicit instantiation of the definitions of unscoped member enumerations and member anonymous unions."

to mean that the only the declaration but not the initializer of the static const data member declared in a class is instantiated, and resolving other bug reports like this one as invalid (for example, bug 58848 and bug 70820).

But the interpretation in the stackoverflow response that the initializer expression for a static const data member initialized in the class is actually part of the declaration does seem to be supported by the text in class.static.data, p3 (quoted below) and by two other implementations I tried (Clang 3.8 and EDG eccp 4.11):

"A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [...] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer."

I CC Jason for his opinion.
Comment 2 Michele Caini 2016-06-16 06:21:12 UTC
The fact that it compiles it is misleading at least.
Consider the following code:

    #include<type_traits>

    template<typename...>
    using void_t = void;

    template<typename, typename = void_t<>>
    struct has_foo: std::false_type { };

    template<typename T>
    struct has_foo<T, decltype(T::foo)>: std::true_type { };

    struct S: has_foo<S> {
        static constexpr int foo = 42;
    };

    int main() {
        // S does not have foo...
        static_assert(not S::value, "!");
        // ... really?
        static_assert(S::foo == 42, "!");
    }

The result is unexpected indeed.
So subtle an issue to find, that one.
Comment 3 bogdan 2016-07-23 13:36:42 UTC
Following the adoption of P0386R2 into the working draft, the declaration of bar is now a definition, because bar is an inline variable.

This makes GCC's behaviour standard-conforming in C++1z mode as far as I can tell. However, I think a fix should still be applied to C++14 and 11 modes, in the interest of portability.
Comment 4 Drea Pinski 2021-07-23 02:44:49 UTC
Confirmed.