Out of curiousity, I've played with this a little bit. Given constexpr int foo (int x) { int a; a = 5; return x + a; } static_assert (foo (2) == 7); constexpr int bar (int x) { const int a; // { dg-error "" } constexpr int b; // { dg-error "" } return x; } constexpr int baz (int x) { int a; return x + a; // { dg-error "" } } constexpr int a = baz (5); constexpr int qux () { struct S { int a = -5; int b; } s; return s.a; } static_assert (qux () == -5); constexpr int quux () { struct S { int a = 9; int b; } s; return s.b; // { dg-error "" } } constexpr int b = quux (); the following patch doesn't diagnose the quux bug of using uninitialized s.b. For some reason CONSTRUCTOR_NO_CLEARING is not set and thus we value-initialize instead of diagnosing. --- gcc/cp/decl.c.jj 2019-08-05 09:58:07.713491022 +0200 +++ gcc/cp/decl.c 2019-08-05 10:22:28.534127984 +0200 @@ -5742,8 +5742,10 @@ check_for_uninitialized_const_var (tree 7.1.6 */ if (VAR_P (decl) && !TYPE_REF_P (type) - && (constexpr_context_p - || CP_TYPE_CONST_P (type) || var_in_constexpr_fn (decl)) + && (CP_TYPE_CONST_P (type) + || (cxx_dialect < cxx2a + && (constexpr_context_p + || var_in_constexpr_fn (decl)))) && !DECL_NONTRIVIALLY_INITIALIZED_P (decl)) { tree field = default_init_uninitialized_part (type); @@ -5752,7 +5754,7 @@ check_for_uninitialized_const_var (tree bool show_notes = true; - if (!constexpr_context_p) + if (!constexpr_context_p || cxx_dialect >= cxx2a) { if (CP_TYPE_CONST_P (type)) { --- gcc/cp/constexpr.c.jj 2019-08-05 09:57:55.147683227 +0200 +++ gcc/cp/constexpr.c 2019-08-05 10:51:56.954215860 +0200 @@ -814,13 +814,15 @@ cx_check_missing_mem_inits (tree ctype, continue; if (ANON_AGGR_TYPE_P (TREE_TYPE (field))) { - /* Recurse to check the anonummous aggregate member. */ + /* Recurse to check the anonymous aggregate member. */ bad |= cx_check_missing_mem_inits (TREE_TYPE (field), NULL_TREE, complain); if (bad && !complain) return true; continue; } + if (cxx_dialect >= cxx2a) + continue; ftype = strip_array_types (TREE_TYPE (field)); if (type_has_constexpr_default_constructor (ftype)) { @@ -6617,8 +6619,9 @@ potential_constant_expression_1 (tree t, "%<thread_local%> in %<constexpr%> context", tmp); return false; } - else if (!check_for_uninitialized_const_var - (tmp, /*constexpr_context_p=*/true, flags)) + else if (cxx_dialect < cxx2a + && !check_for_uninitialized_const_var + (tmp, /*constexpr_context_p=*/true, flags)) return false; } return RECUR (tmp, want_rval); --- gcc/cp/method.c.jj 2019-05-20 23:33:13.818084173 +0200 +++ gcc/cp/method.c 2019-08-05 10:46:07.057545848 +0200 @@ -1410,7 +1410,9 @@ walk_field_subobs (tree fields, special_ /* For an implicitly-defined default constructor to be constexpr, every member must have a user-provided default constructor or an explicit initializer. */ - if (constexpr_p && !CLASS_TYPE_P (mem_type) + if (constexpr_p + && cxx_dialect < cxx2a + && !CLASS_TYPE_P (mem_type) && TREE_CODE (DECL_CONTEXT (field)) != UNION_TYPE) { *constexpr_p = false; Not really sure with the removal of diagnostics for constexpr ctors not initializing all non-static data members for -std=c++2a, shall we diagnose somewhere else if a const or constexpr variable is initialized with such a constructor? And part of this change shall be bumping for cxx_dialect >= cxx2a of __cpp_constexpr value.
Note, I won't have time soon to work on this further, so if anyone wants to take it over, reusing or not reusing the above patch, feel free.
The quux case: CONSTRUCTOR_NO_CLEARING is cleared here 2154 /* The result of a constexpr function must be completely initialized. */ 2155 if (TREE_CODE (result) == CONSTRUCTOR) 2156 clear_no_implicit_zero (result); but we can no longer assume that a constexpr constructor has initialized all the members.
(In reply to Marek Polacek from comment #2) > The quux case: CONSTRUCTOR_NO_CLEARING is cleared here > > 2154 /* The result of a constexpr function must be completely initialized. > */ > 2155 if (TREE_CODE (result) == CONSTRUCTOR) > 2156 clear_no_implicit_zero (result); > > but we can no longer assume that a constexpr constructor has initialized all > the members. I'd think we can still assume it for non-constructors (because if a function returns some aggregate, it went through the lvalue to rvalue conversion and therefore shouldn't refer to uninitialized members). Constructors don't really return the object they are initializing. Though, perhaps we should make a difference between constructors that do have member initializers for all members and those that don't.
(In reply to Jakub Jelinek from comment #3) > (In reply to Marek Polacek from comment #2) > > The quux case: CONSTRUCTOR_NO_CLEARING is cleared here > > > > 2154 /* The result of a constexpr function must be completely initialized. > > */ > > 2155 if (TREE_CODE (result) == CONSTRUCTOR) > > 2156 clear_no_implicit_zero (result); > > > > but we can no longer assume that a constexpr constructor has initialized all > > the members. > > I'd think we can still assume it for non-constructors (because if a function > returns some aggregate, it went through the lvalue to rvalue conversion and > therefore shouldn't refer to uninitialized members). Constructors don't > really > return the object they are initializing. Though, perhaps we should make a > difference between constructors that do have member initializers for all > members and those that don't. I'm experimenting with the last -- add an allow_missing param to cx_check_missing_mem_inits and don't clear the flag when there are inits missing. Something to consider: unions will never have initialized all its members.
(In reply to Marek Polacek from comment #4) > (In reply to Jakub Jelinek from comment #3) > > (In reply to Marek Polacek from comment #2) > > > The quux case: CONSTRUCTOR_NO_CLEARING is cleared here > > > > > > 2154 /* The result of a constexpr function must be completely initialized. > > > */ > > > 2155 if (TREE_CODE (result) == CONSTRUCTOR) > > > 2156 clear_no_implicit_zero (result); > > > > > > but we can no longer assume that a constexpr constructor has initialized all > > > the members. > > > > I'd think we can still assume it for non-constructors (because if a function > > returns some aggregate, it went through the lvalue to rvalue conversion and > > therefore shouldn't refer to uninitialized members). Constructors don't > > really > > return the object they are initializing. Though, perhaps we should make a > > difference between constructors that do have member initializers for all > > members and those that don't. > > I'm experimenting with the last -- add an allow_missing param to > cx_check_missing_mem_inits and don't clear the flag when there are inits > missing. ... unless the initializer is { } in which case we're performing aggregate-init or value-init so the flag ought to be cleared.
Current patch (modulo testsuite changes) that seems to work pretty well: diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c index 76d1e4a380e..9766d7f96c4 100644 --- gcc/c-family/c-cppbuiltin.c +++ gcc/c-family/c-cppbuiltin.c @@ -972,7 +972,8 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_fold_expressions=201603L"); cpp_define (pfile, "__cpp_nontype_template_args=201411L"); cpp_define (pfile, "__cpp_range_based_for=201603L"); - cpp_define (pfile, "__cpp_constexpr=201603L"); + if (cxx_dialect <= cxx17) + cpp_define (pfile, "__cpp_constexpr=201603L"); cpp_define (pfile, "__cpp_if_constexpr=201606L"); cpp_define (pfile, "__cpp_capture_star_this=201603L"); cpp_define (pfile, "__cpp_inline_variables=201606L"); @@ -991,6 +992,7 @@ c_cpp_builtins (cpp_reader *pfile) { /* Set feature test macros for C++2a. */ cpp_define (pfile, "__cpp_conditional_explicit=201806L"); + cpp_define (pfile, "__cpp_constexpr=201907L"); cpp_define (pfile, "__cpp_constinit=201907L"); cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806L"); cpp_define (pfile, "__cpp_impl_destroying_delete=201806L"); diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c index 8c79b0484fc..5bfbc1f7ce5 100644 --- gcc/cp/constexpr.c +++ gcc/cp/constexpr.c @@ -765,7 +765,7 @@ massage_constexpr_body (tree fun, tree body) bases/fields are uninitialized, and complain if COMPLAIN. */ static bool -cx_check_missing_mem_inits (tree ctype, tree body, bool complain) +cx_check_missing_mem_inits (tree ctype, tree body, bool complain, bool allow_missing = true) { unsigned nelts = 0; @@ -815,13 +815,15 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain) continue; if (ANON_AGGR_TYPE_P (TREE_TYPE (field))) { - /* Recurse to check the anonummous aggregate member. */ + /* Recurse to check the anonymous aggregate member. */ bad |= cx_check_missing_mem_inits - (TREE_TYPE (field), NULL_TREE, complain); + (TREE_TYPE (field), NULL_TREE, complain, allow_missing); if (bad && !complain) return true; continue; } + if (cxx_dialect >= cxx2a && allow_missing) + continue; ftype = strip_array_types (TREE_TYPE (field)); if (type_has_constexpr_default_constructor (ftype)) { @@ -829,6 +831,10 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain) A constexpr ctor that isn't trivial should have been added in by now. */ gcc_checking_assert (!TYPE_HAS_COMPLEX_DFLT (ftype) + /* If we're only checking for missing + inits, non-trivial ctors have not been + added yet. */ + || (cxx_dialect >= cxx2a && !allow_missing) || errorcount != 0); continue; } @@ -847,7 +853,7 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain) { /* Check the anonymous aggregate initializer is valid. */ bad |= cx_check_missing_mem_inits - (TREE_TYPE (index), CONSTRUCTOR_ELT (body, i)->value, complain); + (TREE_TYPE (index), CONSTRUCTOR_ELT (body, i)->value, complain, allow_missing); if (bad && !complain) return true; } @@ -2150,8 +2156,18 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, } /* The result of a constexpr function must be completely initialized. */ + // XXX not in C++20 constructor + // XXX pedwarn somewhere if (TREE_CODE (result) == CONSTRUCTOR) - clear_no_implicit_zero (result); + { + clear_no_implicit_zero (result); + if (cxx_dialect >= cxx2a + && DECL_CONSTRUCTOR_P (fun) + && DECL_DECLARED_CONSTEXPR_P (fun) + && NON_UNION_CLASS_TYPE_P (TREE_TYPE (result)) + && cx_check_missing_mem_inits (TREE_TYPE (result), result, false, false)) + CONSTRUCTOR_NO_CLEARING (result) = true; + } pop_cx_call_context (); return result; @@ -2903,8 +2919,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, /* Not found. */ - if (TREE_CODE (ary) == CONSTRUCTOR - && CONSTRUCTOR_NO_CLEARING (ary)) + if (TREE_CODE (ary) == CONSTRUCTOR && CONSTRUCTOR_NO_CLEARING (ary)) { /* 'ary' is part of the aggregate initializer we're currently building; if there's no initializer for this element yet, diff --git gcc/cp/decl.c gcc/cp/decl.c index 86e38f4af69..ab6da6080a5 100644 --- gcc/cp/decl.c +++ gcc/cp/decl.c @@ -5835,8 +5835,12 @@ check_for_uninitialized_const_var (tree decl, bool constexpr_context_p, 7.1.6 */ if (VAR_P (decl) && !TYPE_REF_P (type) - && (constexpr_context_p - || CP_TYPE_CONST_P (type) || var_in_constexpr_fn (decl)) + && (CP_TYPE_CONST_P (type) + /* C++20 permits trivial default initialization in constexpr + context (P1331R2). */ + || (cxx_dialect < cxx2a + && (constexpr_context_p + || var_in_constexpr_fn (decl)))) && !DECL_NONTRIVIALLY_INITIALIZED_P (decl)) { tree field = default_init_uninitialized_part (type); @@ -5845,7 +5849,7 @@ check_for_uninitialized_const_var (tree decl, bool constexpr_context_p, bool show_notes = true; - if (!constexpr_context_p) + if (!constexpr_context_p || cxx_dialect >= cxx2a) { if (CP_TYPE_CONST_P (type)) { diff --git gcc/cp/method.c gcc/cp/method.c index acba6c6da8c..f01ea6a9128 100644 --- gcc/cp/method.c +++ gcc/cp/method.c @@ -1985,10 +1985,12 @@ walk_field_subobs (tree fields, special_function_kind sfk, tree fnname, if (bad && deleted_p) *deleted_p = true; - /* For an implicitly-defined default constructor to be constexpr, - every member must have a user-provided default constructor or - an explicit initializer. */ - if (constexpr_p && !CLASS_TYPE_P (mem_type) + /* Before C++20, for an implicitly-defined default constructor to be + constexpr, every member must have a user-provided default constructor + or an explicit initializer. */ + if (constexpr_p + && cxx_dialect < cxx2a + && !CLASS_TYPE_P (mem_type) && TREE_CODE (DECL_CONTEXT (field)) != UNION_TYPE) { *constexpr_p = false;
https://gcc.gnu.org/ml/gcc-patches/2019-11/msg01599.html
Author: mpolacek Date: Thu Dec 5 20:13:03 2019 New Revision: 279019 URL: https://gcc.gnu.org/viewcvs?rev=279019&root=gcc&view=rev Log: PR c++/91353 - P1331R2: Allow trivial default init in constexpr contexts. This patch implements C++20 P1331, allowing trivial default initialization in constexpr contexts. * c-cppbuiltin.c (c_cpp_builtins): Adjust the value of __cpp_constexpr. * class.c (trivial_default_constructor_is_constexpr): Return true in C++20. * constexpr.c (cx_check_missing_mem_inits): Allow missing field initializers in C++20. (cxx_eval_call_expression): Don't clear CONSTRUCTOR_NO_CLEARING for constexpr constructors in C++20. (reduced_constant_expression_p): Don't set FIELD for union and array types. Skip empty class fields without initializers. * decl.c (check_for_uninitialized_const_var): Permit trivial default initialization in constexpr. (next_initializable_field): Don't skip vptr fields. * method.c (walk_field_subobs): Still consider a constructor that doesn't initialize all the members constexpr. * g++.dg/cpp0x/constexpr-array6.C: Adjust dg-error. * g++.dg/cpp0x/constexpr-ctor.C: Likewise. * g++.dg/cpp0x/constexpr-diag3.C: Likewise. * g++.dg/cpp0x/constexpr-diag4.C: Likewise. * g++.dg/cpp0x/constexpr-ex3.C: Likewise. * g++.dg/cpp0x/constexpr-template2.C: Likewise. * g++.dg/cpp0x/constexpr-union2.C: Likewise. * g++.dg/cpp0x/lambda/lambda-mangle.C: Rip out a piece of code ... * g++.dg/cpp0x/lambda/lambda-mangle6.C: ... and put it here. * g++.dg/cpp0x/pr79118.C: Adjust dg-error. * g++.dg/cpp1y/constexpr-83921-3.C: Likewise. * g++.dg/cpp1y/constexpr-neg1.C: Likewise. * g++.dg/cpp1z/constexpr-lambda12.C: Likewise. * g++.dg/cpp1z/feat-cxx1z.C: Use -std=c++17. * g++.dg/cpp2a/constexpr-init1.C: New test. * g++.dg/cpp2a/constexpr-init2.C: New test. * g++.dg/cpp2a/constexpr-init3.C: New test. * g++.dg/cpp2a/constexpr-init4.C: New test. * g++.dg/cpp2a/constexpr-init5.C: New test. * g++.dg/cpp2a/constexpr-init6.C: New test. * g++.dg/cpp2a/constexpr-init7.C: New test. * g++.dg/cpp2a/constexpr-init8.C: New test. * g++.dg/cpp2a/constexpr-init9.C: New test. * g++.dg/cpp2a/constexpr-init10.C: New test. * g++.dg/cpp2a/constexpr-init11.C: New test. * g++.dg/cpp2a/constexpr-init12.C: New test. * g++.dg/cpp2a/constexpr-init13.C: New test. * g++.dg/cpp2a/constexpr-init14.C: New test. * g++.dg/cpp2a/constexpr-init15.C: New test. * g++.dg/cpp2a/constexpr-try5.C: Adjust dg-error. * g++.dg/cpp2a/feat-cxx2a.C: Test __cpp_constexpr. * g++.dg/cpp2a/lambda-mangle.C: New test. * g++.dg/debug/dwarf2/pr44641.C: Skip for c++2a. * g++.dg/ext/stmtexpr21.C: Adjust dg-error. Added: trunk/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle6.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init10.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init11.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init12.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init13.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init14.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init15.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init2.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init3.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init4.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init5.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init6.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init7.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init8.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-init9.C trunk/gcc/testsuite/g++.dg/cpp2a/lambda-mangle.C Modified: trunk/gcc/c-family/ChangeLog trunk/gcc/c-family/c-cppbuiltin.c trunk/gcc/cp/ChangeLog trunk/gcc/cp/class.c trunk/gcc/cp/constexpr.c trunk/gcc/cp/decl.c trunk/gcc/cp/method.c trunk/gcc/testsuite/ChangeLog trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-array6.C trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor.C trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-diag4.C trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-ex3.C trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-template2.C trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-union2.C trunk/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C trunk/gcc/testsuite/g++.dg/cpp0x/pr79118.C trunk/gcc/testsuite/g++.dg/cpp1y/constexpr-83921-3.C trunk/gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C trunk/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda12.C trunk/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C trunk/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C trunk/gcc/testsuite/g++.dg/debug/dwarf2/pr44641.C trunk/gcc/testsuite/g++.dg/ext/stmtexpr21.C
Implemented for GCC 10.