Sửa lỗi non-static data member initializers only available with or năm 2024

In Visual Studio 2013 we shipped an implementation of non-static data member initialization (hereby referred to as “NSDMI”), which is a feature that allows code such as the following:

class C { int n = 42; };

Here, n is a non-static member of the class, and it is initialized directly in its declaration, and not via a constructor.

Unfortunately, the code in our compiler (some of it written 30 years ago) for handling initialization suffers from many longstanding architectural limitations. This code was sufficient for C++03-style initialization, but NSDMI, a C++11 feature, caused it to exhibit these limitations as severe bugs. One of these bugs is described in the MSDN article about error C2797. List initialization inside a non-static data member initializer would have been silently converted into a function call, resulting in incorrect behavior. That is, if one writes:

include <vector>

class S { std::vector<int> v{ 1, 2 }; };

The compiler would have treated the code above as if the user had written:

include <vector>

class S { std::vector<int> v = std::vector<int>(1, 2); };

Instead of initializing the vector with two given elements, the Visual Studio 2013 RTM compiler initializes it with length one and a single element. We’ve received countless bug reports about this behavior in the past year. Furthermore, this is not the only issue that prevents initializer lists from functioning correctly.

We originally planned to fix this bug in an update to Visual Studio 2013, but from an engineering perspective, the right thing to do is to avoid another kludge and thoroughly address the handling of initialization. But overhauling compiler architecture is a massive task due to the amount of fundamental code that needs to be modified. We could not risk creating incompatibilities or large bug tails in an update, so a correct implementation of NSDMI could only be shipped in a major release.

Meanwhile, we still needed to address the steady stream of incoming feedback about bad code generation, so we made the hard decision of creating error C2797. This error guides users towards avoiding the issue and working around it by writing explicit constructions of inner lists, as the MSDN article suggests. The following code, for example, works as expected:

include <vector>

class S { std::vector<int> v = std::vector<int> {1, 2}; };

We are aware that the release notes for Visual Studio 2013 Update 3 did not include a notification about this new error. That was a mistake and we sincerely apologize for any confusion this has caused. However, C2797 will continue to be present in all future versions of Visual Studio 2013, so we recommend that you immediately make use of the provided workarounds. The architectural changes and fixes for initialization will be included in Visual Studio “14” RTM.

Andrew Pinski 2021-11-21 14:50:24 UTC

This is macro tracking which is getting in the way of emitting the warning as NULL is from a system header, there is a dup of this bug already.

Andrew Pinski 2021-11-21 14:52:45 UTC

That is GCC does warning about the following case: struct test {

int x = 0;
}; int main() {}

Jonathan Wakely 2021-11-21 17:02:08 UTC

(In reply to Andrew Pinski from [comment

2](https://gcc.gnu.org/bugzilla/show%5Fbug.cgi?id=103347

c2))
That is GCC does warning about the following case:

struct test { int x = 0; }; int main() {} And you get an error with -pedantic-errors

Andrew Pinski 2021-11-22 09:37:26 UTC

Actually this is a dup of bug 57201. This bug has been marked as a duplicate of bug 57201

Andrew Pinski 2021-11-22 09:38:43 UTC

Let's mark this as a dup of bug 77299. This bug has been marked as a duplicate of bug 77299

Andrew Pinski 2021-11-22 09:47:18 UTC

Actually let's reopen this one too. It might be a different bug from PR 77299 ...

Jakub Jelinek 2021-11-22 16:43:52 UTC

Perhaps a faster way to fix this would be to try to warn on the = token rather than on input_location which happens to be the location of NULL after cp_parser_cache_defarg went through it. For struct test {

int x = 1 + 2; //invalid in C++03 mode
}; we strangely warn with caret on the 2, for int x = (1 + 2); with caret on ) etc. maybe_warn_cpp0x doesn't really take a location_t though, probably it should. And grokdeclarator isn't called with the actual initializer (DEFERRED_PARSE) from which we could dig up the location of the first token.

Marek Polacek 2021-11-22 18:19:04 UTC

(In reply to Jakub Jelinek from [comment

8](https://gcc.gnu.org/bugzilla/show%5Fbug.cgi?id=103347

c8))
And grokdeclarator isn't called with the actual initializer (DEFERRED_PARSE)

from which we could dig up the location of the first token. True, though cp_parser_member_declaration has initializer_token_start which carries the location of the '='. I suppose we could add another location_t to cp_declarator for init-declarators, and then just use it. For which I have a patch: $ ./cc1plus -quiet 103347.C -std=c++03 103347.C:3:13: warning: non-static data member initializers only available with ‘-std=c++11’ or ‘-std=gnu++11’ [-Wc++11-extensions]

3 |     void *x = NULL; //invalid in C++03 mode
  |             ^
GCC Commits 2021-11-24 05:24:18 UTC

The trunk branch has been updated by Marek Polacek <[email protected]>: <https://gcc.gnu.org/g:d71d019f63ed5d3fdb34579023bafa4dcf323f2c> commit r12-5488-gd71d019f63ed5d3fdb34579023bafa4dcf323f2c Author: Marek Polacek <[email protected]> Date: Mon Nov 22 14:09:25 2021 -0500
c++: Fix missing NSDMI diagnostic in C++98 [[PR103347](https://gcc.gnu.org/bugzilla/show%5Fbug.cgi?id=103347)]
Here the problem is that we aren't detecting a NSDMI in C++98:
struct A {
  void *x = NULL;
};
because maybe_warn_cpp0x uses input_location and that happens to point
to NULL which comes from a system header.  Jakub suggested changing the
location to the '=', thereby avoiding the system header problem.  To
that end, I've added a new location_t member into cp_declarator.  This
member is used when this declarator is part of an init-declarator.  The
rest of the changes is obvious.  I've also taken the liberty of adding
loc_or_input_loc, since I want to avoid checking for UNKNOWN_LOCATION.
        [PR c++/103347](https://gcc.gnu.org/bugzilla/show%5Fbug.cgi?id=103347)
gcc/cp/ChangeLog:
  • cp-tree.h (struct cp_declarator): Add a location_t member.
            (maybe_warn_cpp0x): Add a location_t parameter with a default argument.
            (loc_or_input_loc): New.
  • decl.c (grokdeclarator): Use loc_or_input_loc. Pass init_loc down to maybe_warn_cpp0x.
  • error.c (maybe_warn_cpp0x): Add a location_t parameter. Use it.
  • parser.c (make_declarator): Initialize init_loc. (cp_parser_member_declaration): Set init_loc. (cp_parser_condition): Likewise. (cp_parser_init_declarator): Likewise. (cp_parser_parameter_declaration): Likewise. gcc/testsuite/ChangeLog:
  • g++.dg/cpp0x/nsdmi-warn1.C: New test.
  • g++.dg/cpp0x/nsdmi-warn1.h: New file.

Marek Polacek 2021-11-24 05:26:22 UTC

Fixed in GCC 12.

ensadc 2021-12-01 02:51:25 UTC

That is GCC does warning about the following case: struct test {

int x = 0;
}; int main() {}

0