https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104629

            Bug ID: 104629
           Summary: Constexpr static functions and variables in templates,
                    in the wrong order and under specific circumstances
                    produces ICEs under some versions, compiles (but
                    shouldn't) on others
           Product: gcc
           Version: 11.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: ahajha at gmail dot com
  Target Milestone: ---

The following code, as far as I can tell, is invalid C++. Recent versions of
other compilers reject this, citing that foo() has is not found at its use in
Inner().

template<class...>
struct Outer
{
 struct Inner
 {
  constexpr Inner() { foo(); }
 };

 constexpr static auto inner = Inner{};

 constexpr void foo();
};

int main()
{
 auto x = Outer<>::inner;
}

This code produces ICEs under versions 7.x, 8.x, and 11.x.

Under 7.x and 8.x, the error is as follows:

<source>: In instantiation of 'constexpr Outer< <template-parameter-1-1>
>::Inner::Inner() [with <template-parameter-1-1> = {}]':
<source>:9:24:   recursively required from 'constexpr const Outer<>::Inner
Outer<>::inner'
<source>:9:24:   required from 'struct Outer<>'
<source>:16:18:   required from here
<source>:6:23: internal compiler error: in finish_expr_stmt, at
cp/semantics.c:678
   constexpr Inner() { foo(); }
                       ^~~
mmap: Invalid argument

(The last line "mmap: Invalid argument", seems to randomly appear with some
minor versions and not with others)

Under 11.2.0, the error is as follows:

ice1.cpp: In instantiation of ‘constexpr Outer< <template-parameter-1-1>
>::Inner::Inner() [with <template-parameter-1-1> = {}]’:
ice1.cpp:9:24:   recursively required from ‘constexpr const Outer<>::Inner
Outer<>::inner’
ice1.cpp:9:24:   required from ‘struct Outer<>’
ice1.cpp:16:18:   required from here
ice1.cpp:6:37: internal compiler error: in finish_expr_stmt, at
cp/semantics.c:860
    6 |                 constexpr Inner() { foo(); }
      |                                     ^~~
0x6fc478 finish_expr_stmt(tree_node*)
        ../../src/gcc/cp/semantics.c:860
0x116c048 tsubst_expr(tree_node*, tree_node*, int, tree_node*, bool)
        ../../src/gcc/cp/pt.c:18233
0x116beb5 tsubst_expr(tree_node*, tree_node*, int, tree_node*, bool)
        ../../src/gcc/cp/pt.c:18550
0x116bf43 tsubst_expr(tree_node*, tree_node*, int, tree_node*, bool)
        ../../src/gcc/cp/pt.c:18199
0x116beb5 tsubst_expr(tree_node*, tree_node*, int, tree_node*, bool)
        ../../src/gcc/cp/pt.c:18550
0x1228bd0 instantiate_body
        ../../src/gcc/cp/pt.c:25876
0x1227ffa instantiate_decl(tree_node*, bool, bool)
        ../../src/gcc/cp/pt.c:26170
0x10145fd instantiate_cx_fn_r
        ../../src/gcc/cp/constexpr.c:7085
0xf49a8a walk_tree_1(tree_node**, tree_node* (*)(tree_node**, int*, void*),
void*, hash_set<tree_node*, false, default_hash_traits<tree_node*> >*,
tree_node* (*)(tree_node**, int*, tree_node* (*)(tree_node**, int*, void*),
void*, hash_set<tree_node*, false, default_hash_traits<tree_node*> >*))
        ../../src/gcc/tree.c:12108
0xf49c51 walk_tree_1(tree_node**, tree_node* (*)(tree_node**, int*, void*),
void*, hash_set<tree_node*, false, default_hash_traits<tree_node*> >*,
tree_node* (*)(tree_node**, int*, tree_node* (*)(tree_node**, int*, void*),
void*, hash_set<tree_node*, false, default_hash_traits<tree_node*> >*))
        ../../src/gcc/tree.c:12456
0xf49692 walk_tree_without_duplicates_1(tree_node**, tree_node*
(*)(tree_node**, int*, void*), void*, tree_node* (*)(tree_node**, int*,
tree_node* (*)(tree_node**, int*, void*), void*, hash_set<tree_node*, false,
default_hash_traits<tree_node*> >*))
        ../../src/gcc/tree.c:12482
0x101054c instantiate_constexpr_fns
        ../../src/gcc/cp/constexpr.c:7105
0x101054c cxx_eval_outermost_constant_expr
        ../../src/gcc/cp/constexpr.c:7259
0x10202ec maybe_constant_value(tree_node*, tree_node*, bool)
        ../../src/gcc/cp/constexpr.c:7518
0xffbe4b fold_non_dependent_expr(tree_node*, int, bool, tree_node*)
        ../../src/gcc/cp/constexpr.c:7644
0xffbe4b store_init_value(tree_node*, tree_node*, vec<tree_node*, va_gc,
vl_embed>**, int)
        ../../src/gcc/cp/typeck2.c:777
0xfeb03b check_initializer
        ../../src/gcc/cp/decl.c:7130
0xfdce84 cp_finish_decl(tree_node*, tree_node*, bool, tree_node*, int)
        ../../src/gcc/cp/decl.c:8064
0x1228354 instantiate_decl(tree_node*, bool, bool)
        ../../src/gcc/cp/pt.c:26130
0x65e75d cp_finish_decl(tree_node*, tree_node*, bool, tree_node*, int)
        ../../src/gcc/cp/decl.c:7783

This code compiles (which I believe it shouldn't) without warnings (other than
x being unused) under version 9.x and 10.x.

All versions were tested using https://godbolt.org/, and GCC 11.2.0 was also
tested locally, the configuration is attached.

Compiler flags used for all tests: -Wall -Wextra -std=c++14
(I believe the same behavior happens under C++17 and 20)

Reply via email to