Bug 91353 - Implement P1331R2: Permitting trivial default initialization in constexpr contexts
Summary: Implement P1331R2: Permitting trivial default initialization in constexpr con...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 9.1.1
: P3 normal
Target Milestone: ---
Assignee: Marek Polacek
URL:
Keywords: patch
Depends on:
Blocks: constexpr 88323
  Show dependency treegraph
 
Reported: 2019-08-05 09:11 UTC by Jakub Jelinek
Modified: 2019-12-05 20:41 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2019-08-05 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jakub Jelinek 2019-08-05 09:11:23 UTC
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.
Comment 1 Jakub Jelinek 2019-08-05 15:00:26 UTC
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.
Comment 2 Marek Polacek 2019-11-13 21:48:45 UTC
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.
Comment 3 Jakub Jelinek 2019-11-14 15:10:29 UTC
(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.
Comment 4 Marek Polacek 2019-11-14 15:16:07 UTC
(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.
Comment 5 Marek Polacek 2019-11-14 15:34:10 UTC
(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.
Comment 6 Marek Polacek 2019-11-14 19:55:14 UTC
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;
Comment 8 Marek Polacek 2019-12-05 20:13:40 UTC
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
Comment 9 Marek Polacek 2019-12-05 20:41:09 UTC
Implemented for GCC 10.