Bug 111261 - No warning for out of order class initialisation when using class initialisers
Summary: No warning for out of order class initialisation when using class initialisers
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 14.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic
Depends on:
Blocks:
 
Reported: 2023-08-31 20:15 UTC by Matt Godbolt
Modified: 2023-09-01 08:19 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2023-09-01 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Matt Godbolt 2023-08-31 20:15:54 UTC
The code:

```
struct S {
  std::size_t len{s.size()};
  std::string s{"A rather long string"};
};

```

warns on clang, but not on GCC. https://compiler-explorer.com/z/5qf3WbdY7

The equivalent code using a constructor _does_ warn on both compilers:

```
struct S {
  std::size_t len;
  std::string s;

  S() : s{"A rather long string"}, len{s.size()} {}
};
```

( https://compiler-explorer.com/z/Wov4Tx93v )
Comment 1 Richard Biener 2023-09-01 07:04:57 UTC
We diagnose this at -O1 now but not -O0, -O2 or -O3.  We rely on the late uninit diagnostic pass here but that's faced with optimized code.  Interestingly an older version of libstdc++ warned at -O2 and -O3 as well.
Comment 2 Richard Biener 2023-09-01 07:27:16 UTC
In fact with trunk we diagnose this properly with -std=c++17 but not -std=c++20.
With -std=c++20 we see

  <bb 2> [local count: 1073741824]:
  _35 = operator new (21);
  __builtin_memcpy (_35, "A rather long string", 20);
  MEM[(char_type &)_35 + 20] = 0;
  operator delete (_35, 21);
  s ={v} {CLOBBER};
  s ={v} {CLOBBER(eol)};
  return 0;

at the point we would be supposed to diagnose the init but you can see
that 's' was elided.

One issue is probably that we elide the standalone S::S() during IPA
(because we inline it) and thus fail to run the late diagnostic passes
on its optimized body:

void S::S (struct S * const this)
{
  struct allocator D.46788;
  struct string * _1;
  struct string * _2;
  long unsigned int _8;

  <bb 2> [local count: 1073741824]:
  _8 = MEM[(const struct basic_string *)this_3(D) + 8B]._M_string_length;
  *this_3(D).len = _8;
  _1 = &this_3(D)->s;
  std::__cxx11::basic_string<char>::basic_string<> (_1, "A rather long string", &D.46788);
  D.46788 ={v} {CLOBBER(eol)};
  return;

of course we lack a start-of-live CLOBBER of *this here (I think I've seen
this before), so we'd fail to diagnose this body as well.
Comment 3 Jonathan Wakely 2023-09-01 08:18:05 UTC
(In reply to Richard Biener from comment #2)
> In fact with trunk we diagnose this properly with -std=c++17 but not
> -std=c++20.

So it's probably because c++17 uses extern template for std::string and C++20 doesn't.

This could probably be done in the front end. The rule about init order is purely lexical, and so if the initializer odr-uses another member declared later, we should warn.
Comment 4 Jonathan Wakely 2023-09-01 08:19:20 UTC
The FE already has to do lookup for s in that initializer, so it knows that another member was found.