Bug 111132 - [12 Regression] Function redeclaration in local scope breaks constant expression evaluation
Summary: [12 Regression] Function redeclaration in local scope breaks constant express...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 13.2.1
: P2 normal
Target Milestone: 12.5
Assignee: Marek Polacek
URL:
Keywords: rejects-valid
Depends on:
Blocks: constexpr
  Show dependency treegraph
 
Reported: 2023-08-24 12:54 UTC by Fedor Chelnokov
Modified: 2025-01-10 19:35 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work: 8.5.0
Known to fail: 9.1.0
Last reconfirmed: 2023-08-24 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Fedor Chelnokov 2023-08-24 12:54:54 UTC
This program

constexpr bool bar(void) {
    return true;
}

constexpr bool foo() {
    constexpr bool bar(void);
    return bar();
}

static_assert( foo() );


is accepted in Clang, but not in GCC, which prints


error: non-constant condition for static assertion
   10 | static_assert( foo() );
      |                ~~~^~
<source>:10:19:   in 'constexpr' expansion of 'foo()'
<source>:7:15: error: 'constexpr bool bar()' used before its definition
    7 |     return bar();
      |            ~~~^~

Online demo: https://godbolt.org/z/3PvoEx61x
Comment 1 Drea Pinski 2023-08-24 18:29:07 UTC
Confirmed.

This used to mostly worked in GCC 8.5.0 and before.

In GCC 8.5.0 and before GCC would produce a bogus warning but the static_assert would worked.

<source>:7:20: warning: inline function 'constexpr bool bar()' used but never defined
     constexpr bool bar(void);
                    ^~~
Comment 2 Patrick Palka 2023-08-24 19:54:06 UTC
The error started with with r9-6136-g43574e4ff2afd4
Comment 3 Marek Polacek 2024-04-01 23:18:17 UTC
I think I have a patch.
Comment 4 GCC Commits 2024-04-05 13:11:16 UTC
The trunk branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>:

https://gcc.gnu.org/g:8c9063825ce726fcbbc067d8a6d062cc2d4acf5e

commit r14-9809-g8c9063825ce726fcbbc067d8a6d062cc2d4acf5e
Author: Marek Polacek <polacek@redhat.com>
Date:   Tue Apr 2 12:59:38 2024 -0400

    c++: constexpr error with fn redecl in local scope [PR111132]
    
    We evaluate constexpr functions on the original, pre-genericization bodies.
    That means that the function body we're evaluating will not have gone
    through cp_genericize_r's "Map block scope extern declarations to visible
    declarations with the same name and type in outer scopes if any".  Here:
    
      constexpr bool bar() { return true; } // #1
      constexpr bool foo() {
        constexpr bool bar(void); // #2
        return bar();
      }
    
    it means that we:
    1) register_constexpr_fundef (#1)
    2) cp_genericize (#1)
       nothing interesting happens
    3) register_constexpr_fundef (foo)
       does copy_fn, so we have two copies of the BIND_EXPR
    4) cp_genericize (foo)
       this remaps #2 to #1, but only on one copy of the BIND_EXPR
    5) retrieve_constexpr_fundef (foo)
       we find it, no problem
    6) retrieve_constexpr_fundef (#2)
       and here #2 isn't found in constexpr_fundef_table, because
       we're working on the BIND_EXPR copy where #2 wasn't mapped to #1
       so we fail.  We've only registered #1.
    
    It should work to use DECL_LOCAL_DECL_ALIAS (which used to be
    extern_decl_map).  We evaluate constexpr functions on pre-cp_fold
    bodies to avoid diagnostic problems, but the remapping I'm proposing
    should not interfere with diagnostics.
    
    This is not a problem for a global scope redeclaration; there we go
    through duplicate_decls which keeps the DECL_UID:
      DECL_UID (olddecl) = olddecl_uid;
    and DECL_UID is what constexpr_fundef_hasher::hash uses.
    
            PR c++/111132
    
    gcc/cp/ChangeLog:
    
            * constexpr.cc (get_function_named_in_call): Use
            cp_get_fndecl_from_callee.
            * cvt.cc (cp_get_fndecl_from_callee): If there's a
            DECL_LOCAL_DECL_ALIAS, use it.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp0x/constexpr-redeclaration3.C: New test.
            * g++.dg/cpp0x/constexpr-redeclaration4.C: New test.
Comment 5 Marek Polacek 2024-04-05 13:26:47 UTC
Fixed on trunk so far.
Comment 6 Richard Biener 2024-07-19 13:21:23 UTC
GCC 11 branch is being closed.
Comment 7 GCC Commits 2025-01-10 19:35:04 UTC
The releases/gcc-13 branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>:

https://gcc.gnu.org/g:294140d752fc9a3a790497da9f1e968e9849b40f

commit r13-9296-g294140d752fc9a3a790497da9f1e968e9849b40f
Author: Marek Polacek <polacek@redhat.com>
Date:   Tue Apr 2 12:59:38 2024 -0400

    c++: constexpr error with fn redecl in local scope [PR111132]
    
    We evaluate constexpr functions on the original, pre-genericization bodies.
    That means that the function body we're evaluating will not have gone
    through cp_genericize_r's "Map block scope extern declarations to visible
    declarations with the same name and type in outer scopes if any".  Here:
    
      constexpr bool bar() { return true; } // #1
      constexpr bool foo() {
        constexpr bool bar(void); // #2
        return bar();
      }
    
    it means that we:
    1) register_constexpr_fundef (#1)
    2) cp_genericize (#1)
       nothing interesting happens
    3) register_constexpr_fundef (foo)
       does copy_fn, so we have two copies of the BIND_EXPR
    4) cp_genericize (foo)
       this remaps #2 to #1, but only on one copy of the BIND_EXPR
    5) retrieve_constexpr_fundef (foo)
       we find it, no problem
    6) retrieve_constexpr_fundef (#2)
       and here #2 isn't found in constexpr_fundef_table, because
       we're working on the BIND_EXPR copy where #2 wasn't mapped to #1
       so we fail.  We've only registered #1.
    
    It should work to use DECL_LOCAL_DECL_ALIAS (which used to be
    extern_decl_map).  We evaluate constexpr functions on pre-cp_fold
    bodies to avoid diagnostic problems, but the remapping I'm proposing
    should not interfere with diagnostics.
    
    This is not a problem for a global scope redeclaration; there we go
    through duplicate_decls which keeps the DECL_UID:
      DECL_UID (olddecl) = olddecl_uid;
    and DECL_UID is what constexpr_fundef_hasher::hash uses.
    
            PR c++/111132
    
    gcc/cp/ChangeLog:
    
            * constexpr.cc (get_function_named_in_call): Use
            cp_get_fndecl_from_callee.
            * cvt.cc (cp_get_fndecl_from_callee): If there's a
            DECL_LOCAL_DECL_ALIAS, use it.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp0x/constexpr-redeclaration3.C: New test.
            * g++.dg/cpp0x/constexpr-redeclaration4.C: New test.
    
    (cherry picked from commit 8c9063825ce726fcbbc067d8a6d062cc2d4acf5e)
Comment 8 Marek Polacek 2025-01-10 19:35:26 UTC
Fixed.