On 12/1/19 8:09 PM, Marek Polacek wrote:
On Thu, Nov 28, 2019 at 11:29:20PM -0500, Jason Merrill wrote:
Sounds like reduced_constant_expression_p needs to deal better with empty
bases.

This got a bit complicated because it also needs to handle unions and
now we also need to heed vptr.  But the following seems to work.

(I'll skip the story about a bogus error I hit when building cmcstl2 and
the need to debug a ~90,000 LOC test, because creduce got stuck reducing
it.)

Bootstrapped/regtested on x86_64-linux, built Boost and cmcstl2.

Ok?

2019-12-01  Marek Polacek  <pola...@redhat.com>
            Jakub Jelinek  <ja...@redhat.com>

        PR c++/91353 - P1331R2: Allow trivial default init 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.  Adjust calls to next_initializable_field.
        * cp-tree.h (next_initializable_field): Adjust declaration.
        * decl.c (check_for_uninitialized_const_var): Permit trivial default
        initialization in constexpr.
        (next_initializable_field): Add a bool parameter.  Use it.
        * 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-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.

diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index 6491545bc3b..680087cd254 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -975,7 +975,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");
@@ -997,6 +998,7 @@ c_cpp_builtins (cpp_reader *pfile)
          cpp_define (pfile, "__cpp_init_captures=201803L");
          cpp_define (pfile, "__cpp_generic_lambdas=201707L");
          cpp_define (pfile, "__cpp_designated_initializers=201707L");
+         cpp_define (pfile, "__cpp_constexpr=201907L");
          cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L");
          cpp_define (pfile, "__cpp_conditional_explicit=201806L");
          cpp_define (pfile, "__cpp_consteval=201811L");
diff --git gcc/cp/class.c gcc/cp/class.c
index f36f75fa0db..d8bb44990b7 100644
--- gcc/cp/class.c
+++ gcc/cp/class.c
@@ -5288,8 +5288,14 @@ trivial_default_constructor_is_constexpr (tree t)
    /* A defaulted trivial default constructor is constexpr
       if there is nothing to initialize.  */
    gcc_assert (!TYPE_HAS_COMPLEX_DFLT (t));
-  /* A class with a vptr doesn't have a trivial default ctor.  */
-  return is_really_empty_class (t, /*ignore_vptr*/true);
+  /* A class with a vptr doesn't have a trivial default ctor.
+     In C++20, a class can have transient uninitialized members, e.g.:
+
+       struct S { int i; constexpr S() = default; };
+
+     should work.  */
+  return (cxx_dialect >= cxx2a
+         || is_really_empty_class (t, /*ignore_vptr*/true));
  }
/* Returns true iff class T has a constexpr default constructor. */
diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index ee3ccb9691c..fa8dde9e22b 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -779,7 +779,9 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool 
complain)
if (TREE_CODE (ctype) == UNION_TYPE)
      {
-      if (nelts == 0 && next_initializable_field (field))
+      if (cxx_dialect < cxx2a
+         && nelts == 0
+         && next_initializable_field (field))

Do we want cx_check_missing_mem_inits to do anything in C++20? Or can we return immediately at the beginning?

        {
          if (complain)
            error ("%<constexpr%> constructor for union %qT must "
@@ -2153,15 +2157,27 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, 
tree t,
        entry->result = result;
      }
- /* The result of a constexpr function must be completely initialized. */
-  if (TREE_CODE (result) == CONSTRUCTOR)
+  /* The result of a constexpr function must be completely initialized.
+
+     However, in C++20, a constexpr constructor doesn't necessarily have
+     to initialize all the fields, so we don't clear CONSTRUCTOR_NO_CLEARING
+     in order to detect reading an unitialized object in constexpr instead
+     of value-initializing it.  (reduced_constant_expression_p is expected to
+     take care of clearing the flag.)  */
+  if (TREE_CODE (result) == CONSTRUCTOR
+      && (cxx_dialect < cxx2a
+         || !DECL_CONSTRUCTOR_P (fun)
+         || !DECL_DECLARED_CONSTEXPR_P (fun)))

How can we get here for a non-constexpr function?

      clear_no_implicit_zero (result);
pop_cx_call_context ();
    return result;
  }
-/* FIXME speed this up, it's taking 16% of compile time on sieve testcase. */
+/* Return true if T is a valid constant initializer.  If a CONSTRUCTOR
+   initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be
+   cleared.
+   FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
bool
  reduced_constant_expression_p (tree t)
@@ -2183,8 +2199,17 @@ reduced_constant_expression_p (tree t)
          if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
            /* An initialized vector would have a VECTOR_CST.  */
            return false;
+         else if (cxx_dialect >= cxx2a
+                  /* An ARRAY_TYPE doesn't have any TYPE_FIELDS.  */
+                  && (TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE
+                      /* A union only initializes one member.  */
+                      || TREE_CODE (TREE_TYPE (t)) == UNION_TYPE))
+           field = NULL_TREE;
          else
-           field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)));
+           /* In C++20, skip fields that don't actually need initializing,
+              like empty bases.  */
+           field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)),
+                                             cxx_dialect >= cxx2a);
        }
        else
        field = NULL_TREE;
@@ -2198,7 +2223,8 @@ reduced_constant_expression_p (tree t)
            {
              if (idx != field)
                return false;
-             field = next_initializable_field (DECL_CHAIN (field));
+             field = next_initializable_field (DECL_CHAIN (field),
+                                               cxx_dialect >= cxx2a);
            }
        }
        if (field)
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 7e810b8ee7b..35f3352bdff 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -6541,7 +6541,7 @@ extern bool is_direct_enum_init                   (tree, 
tree);
  extern void initialize_artificial_var         (tree, vec<constructor_elt, 
va_gc> *);
  extern tree check_var_type                    (tree, tree, location_t);
  extern tree reshape_init                        (tree, tree, tsubst_flags_t);
-extern tree next_initializable_field (tree);
+extern tree next_initializable_field           (tree, bool = false);
  extern tree fndecl_declared_return_type               (tree);
  extern bool undeduced_auto_decl                       (tree);
  extern bool require_deduced_type              (tree, tsubst_flags_t = 
tf_warning_or_error);
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 81d73433547..9feba7d5b54 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
+next_initializable_field (tree field, bool skip_empty /*=false*/)
  {
    while (field
         && (TREE_CODE (field) != FIELD_DECL
             || DECL_UNNAMED_BIT_FIELD (field)
             || (DECL_ARTIFICIAL (field)
-                && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field)))))
+                /* In C++17, don't skip base class fields.  */
+                && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field))
+                /* In C++20, don't skip vptr fields.  */
+                && !(cxx_dialect >= cxx2a && DECL_VIRTUAL_P (field)))

I don't think this needs to be specific to C++20; in all language revisions a vptr makes a class non-aggregate, so we'd only get here for a use like that in reduced_constant_expression_p.

+            || (skip_empty
+                && is_really_empty_class (TREE_TYPE (field),

This should probably check DECL_SIZE (field) == size_zero_node instead, since that will properly distinguish between overlapping and non-overlapping data members of empty class type. And please test how this works with data members of empty class type both with and without [[no_unique_address]].

Jason

Reply via email to