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

            Bug ID: 124395
           Summary: [contracts] ICE in check_postconditions_in_redecl when
                    instantiating function template with pre()/post() and
                    empty parameter pack
           Product: gcc
           Version: 16.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: kachalenko.denis at gmail dot com
  Target Milestone: ---

GCC trunk (16.0.1 20260302, x86_64-w64-mingw32) crashes with an internal
compiler error (segfault) when a function template with a parameter pack used
as function parameters has a pre() or post() contract annotation, and the
template is instantiated with an empty pack.

Non-empty pack instantiations compile correctly. All three evaluation semantics
(enforce, observe, ignore) crash identically.

Minimal reproducer:


template<typename... Ts>
void f(Ts...) pre(true) {}
void g() { f(); }

$ g++ -std=c++2c -fcontracts -c file.cpp
Additional test cases:

Member function (also crashes):


template<typename... Ts>
struct S { void f(Ts...) pre(true) {} };
S<> s;
post() (also crashes):


template<typename... Ts>
struct S { int f(Ts...) post(r: r > 0) { return 1; } };
S<> s;
Non-empty pack (compiles successfully):


template<typename... Ts>
void f(Ts...) pre(true) {}
void g() { f(1); }         // OK
void h() { f(1, 2, 3); }   // OK
Expected behavior:

Code compiles without error. P2900R14 (Contracts for C++) places no restriction
on using pre()/post() with parameter packs.

Actual behavior:


file.cpp: In substitution of 'template<class ... Ts> void f(Ts ...) [with Ts =
{}]':
file.cpp:3:13:   required from here
    3 | void g() { f(); }
      |            ~^~
file.cpp:2:6: internal compiler error: Segmentation fault
    2 | void f(Ts...) pre(true) {}
      |      ^
Backtrace:


0x4007d9d3 check_postconditions_in_redecl(tree_node*, tree_node*)
        ../../gcc-source/gcc/cp/contracts.cc:583
0x4007d9d3 check_postconditions_in_redecl(tree_node*, tree_node*)
        ../../gcc-source/gcc/cp/contracts.cc:573
0x40203e93 tsubst_function_decl
        ../../gcc-source/gcc/cp/pt.cc:15304
0x401ff499 tsubst_decl
        ../../gcc-source/gcc/cp/pt.cc:15701
0x401f2593 tsubst(tree_node*, tree_node*, int, tree_node*)
        ../../gcc-source/gcc/cp/pt.cc:16871
0x40216cc1 instantiate_class_template(tree_node*)
        ../../gcc-source/gcc/cp/pt.cc:12984
0x4026d8b5 complete_type(tree_node*)
        ../../gcc-source/gcc/cp/typeck.cc:138
0x4008b100 start_decl_1(tree_node*, bool)
        ../../gcc-source/gcc/cp/decl.cc:6862
Analysis:

check_postconditions_in_redecl iterates parameter lists of the original and
substituted declarations:


tree t1 = FUNCTION_FIRST_USER_PARM (olddecl);
tree t2 = FUNCTION_FIRST_USER_PARM (newdecl);
for (; t1 && t1 != void_list_node;
     t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2))
When a pack expands to zero parameters, newdecl has no user parameters (t2 is
null), while olddecl still has the unexpanded pack parameter. The loop advances
t2 via TREE_CHAIN(t2) when t2 is already null, causing the segfault.

Workaround: Use contract_assert(expr) inside the function body instead of
pre().

Reply via email to