Bug 117501 - [14 Regression] Consteval constructor does not initialize the variable
Summary: [14 Regression] Consteval constructor does not initialize the variable
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 14.2.0
: P2 normal
Target Milestone: 14.3
Assignee: Marek Polacek
URL:
Keywords: wrong-code
Depends on:
Blocks: constexpr
  Show dependency treegraph
 
Reported: 2024-11-08 11:25 UTC by Anton Sanarov
Modified: 2025-05-14 14:40 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work: 13.3.0
Known to fail:
Last reconfirmed: 2024-11-08 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Anton Sanarov 2024-11-08 11:25:02 UTC
See: https://godbolt.org/z/9jvYTfYE4

```
struct QQQ {
    consteval QQQ(std::string_view d): data(d) {}
    std::string_view data;
};

void sink(const QQQ&);

void f() {
    QQQ q("dogsdogsdogs");
    sink(q);
}
```

This code is accepted with warnings

```
<source>: In function 'void f()':
<source>:13:25: warning: statement with no effect [-Wunused-value]
   13 |     QQQ q("dogsdogsdogs");
      |                         ^
<source>:14:9: warning: 'q' may be used uninitialized [-Wmaybe-uninitialized]
   14 |     sink(q);
      |     ~~~~^~~
<source>:10:6: note: by argument 1 of type 'const QQQ&' to 'void sink(const QQQ&)' declared here
   10 | void sink(const QQQ&);
      |      ^~~~
<source>:13:9: note: 'q' declared here
   13 |     QQQ q("dogsdogsdogs");
      |      
```

And no code is generated for the initialization fo variable `q`

GCC 13 and Clang 19 accept this code with no warnings and the correct initialization code is generated.
Comment 1 Marek Polacek 2024-11-08 16:48:07 UTC
Since r14-4140

commit 6851e3423c2b5ec6516efa0677745d25c905e079
Author: Marek Polacek <polacek@redhat.com>
Date:   Thu Aug 31 20:11:50 2023 -0400

    c++: Move consteval folding to cp_fold_r

I see:

117501.C: In function ‘void f()’:
117501.C:10:25:   in ‘constexpr’ expansion of ‘q.QQQ::QQQ(std::basic_string_view<char>(((const char*)"dogsdogsdogs")))’
117501.C:3:40: error: modification of ‘q’ is not a constant expression

so that probably has something to do with this.
Comment 2 Sam James 2024-11-09 04:37:39 UTC
I got the same bisect result (I'd started before seeing your comment).
Comment 3 Marek Polacek 2024-11-11 15:05:56 UTC
Mine then.
Comment 4 Marek Polacek 2025-01-24 21:05:42 UTC
```
constexpr unsigned
length ()
{
  bool __trans_tmp_1 = __builtin_is_constant_evaluated();
  if (__trans_tmp_1)
    return 42;
  return 1;
}
struct basic_string_view {
  constexpr basic_string_view(const char *) : _M_len{length()}, _M_str{} {}
  long _M_len;
  char _M_str;
};
struct QQQ {
  consteval QQQ(basic_string_view d) : data(d) {}
  basic_string_view data;
};
int
main ()
{
  QQQ q("");
  if (q.data._M_len != 42)
    __builtin_abort ();
}
```
Comment 5 Marek Polacek 2025-01-24 22:19:18 UTC
I think the problem is that we evaluate

    struct QQQ q;
  <<cleanup_point <<< Unknown tree: expr_stmt
    QQQ::QQQ (&q, TARGET_EXPR <D.2687, <<< Unknown tree: aggr_init_expr
      5
      __ct_comp 
      D.2687
      (struct basic_string_view *) <<< Unknown tree: void_cst >>>
      (const char *) "" >>>>) >>>>>;

into

    struct QQQ q;
  <<cleanup_point <<< Unknown tree: expr_stmt
    {.data={._M_len=42, ._M_str=0}} >>>>>;

and then the useless expr_stmt is dropped on the floor.  So q isn't initialized.
Comment 6 Marek Polacek 2025-01-24 22:21:26 UTC
And we want this instead:

  struct QQQ q = {.data={._M_len=0, ._M_str=0}};

  <<cleanup_point   struct QQQ q = {.data={._M_len=0, ._M_str=0}};>>;
Comment 7 GCC Commits 2025-01-31 15:25:57 UTC
The trunk branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>:

https://gcc.gnu.org/g:0d97700443b45b947eda40dac7cf4d0397770b87

commit r15-7306-g0d97700443b45b947eda40dac7cf4d0397770b87
Author: Marek Polacek <polacek@redhat.com>
Date:   Mon Jan 27 14:23:22 2025 -0500

    c++: wrong-code with consteval constructor [PR117501]
    
    We've had a wrong-code problem since r14-4140, due to which we
    forget to initialize a variable.
    
    In consteval39.C, we evaluate
    
        struct QQQ q;
      <<cleanup_point <<< Unknown tree: expr_stmt
        QQQ::QQQ (&q, TARGET_EXPR <D.2687, <<< Unknown tree: aggr_init_expr
          5
          __ct_comp
          D.2687
          (struct basic_string_view *) <<< Unknown tree: void_cst >>>
          (const char *) "" >>>>) >>>>>;
    
    into
    
        struct QQQ q;
      <<cleanup_point <<< Unknown tree: expr_stmt
        {.data={._M_len=42, ._M_str=0}} >>>>>;
    
    and then the useless expr_stmt is dropped on the floor, so q isn't
    initialized.  As pre-r14-4140, we need to handle constructors specially.
    
    With this patch, we generate:
    
        struct QQQ q;
      <<cleanup_point <<< Unknown tree: expr_stmt
        q = {.data={._M_len=42, ._M_str=0}} >>>>>;
    
    initializing q properly.
    
            PR c++/117501
    
    gcc/cp/ChangeLog:
    
            * cp-gimplify.cc (cp_build_init_expr_for_ctor): New.
            (cp_fold_immediate_r): Call it.
            (cp_fold): Break out code into cp_build_init_expr_for_ctor.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp2a/consteval39.C: New test.
            * g++.dg/cpp2a/consteval40.C: New test.
    
    Reviewed-by: Jason Merrill <jason@redhat.com>
Comment 8 Marek Polacek 2025-01-31 15:26:36 UTC
Fixed on trunk so far.
Comment 9 Richard Biener 2025-05-11 09:26:32 UTC
Safe enough to backport?
Comment 10 GCC Commits 2025-05-14 14:39:42 UTC
The releases/gcc-14 branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>:

https://gcc.gnu.org/g:1dc1c1e7f0bb3a295eff1bc8c5d4f4d4b2898d50

commit r14-11779-g1dc1c1e7f0bb3a295eff1bc8c5d4f4d4b2898d50
Author: Marek Polacek <polacek@redhat.com>
Date:   Wed May 14 10:38:47 2025 -0400

    c++: wrong-code with consteval constructor [PR117501]
    
    We've had a wrong-code problem since r14-4140, due to which we
    forget to initialize a variable.
    
    In consteval39.C, we evaluate
    
        struct QQQ q;
      <<cleanup_point <<< Unknown tree: expr_stmt
        QQQ::QQQ (&q, TARGET_EXPR <D.2687, <<< Unknown tree: aggr_init_expr
          5
          __ct_comp
          D.2687
          (struct basic_string_view *) <<< Unknown tree: void_cst >>>
          (const char *) "" >>>>) >>>>>;
    
    into
    
        struct QQQ q;
      <<cleanup_point <<< Unknown tree: expr_stmt
        {.data={._M_len=42, ._M_str=0}} >>>>>;
    
    and then the useless expr_stmt is dropped on the floor, so q isn't
    initialized.  As pre-r14-4140, we need to handle constructors specially.
    
    With this patch, we generate:
    
        struct QQQ q;
      <<cleanup_point <<< Unknown tree: expr_stmt
        q = {.data={._M_len=42, ._M_str=0}} >>>>>;
    
    initializing q properly.
    
            PR c++/117501
    
    gcc/cp/ChangeLog:
    
            * cp-gimplify.cc (cp_build_init_expr_for_ctor): New.
            (cp_fold_immediate_r): Call it.
            (cp_fold): Break out code into cp_build_init_expr_for_ctor.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp2a/consteval39.C: New test.
            * g++.dg/cpp2a/consteval40.C: New test.
    
    Reviewed-by: Jason Merrill <jason@redhat.com>
Comment 11 Marek Polacek 2025-05-14 14:40:08 UTC
Fixed.