c++: 'new T[N]' and SFINAE [PR82110]
authorPatrick Palka <ppalka@redhat.com>
Sat, 10 Jul 2021 02:40:07 +0000 (22:40 -0400)
committerPatrick Palka <ppalka@redhat.com>
Sat, 10 Jul 2021 02:40:07 +0000 (22:40 -0400)
Here we're failing to treat 'new T[N]' as erroneous in a SFINAE context
when T isn't default constructible because expand_aggr_init_1 doesn't
communicate to build_aggr_init (its only SFINAE caller) whether the
initialization was actually successful.  To fix this, this patch makes
expand_aggr_init_1 and its subroutine expand_default_init return true on
success, false on failure so that build_aggr_init can properly return
error_mark_node on failure.

PR c++/82110

gcc/cp/ChangeLog:

* init.c (build_aggr_init): Return error_mark_node if
expand_aggr_init_1 returns false.
(expand_default_init): Change return type to bool.  Return false
on error, true on success.
(expand_aggr_init_1): Likewise.

gcc/testsuite/ChangeLog:

* g++.dg/cpp0x/pr78765.C: Expect another conversion failure
diagnostic.
* g++.dg/template/sfinae14.C: Flip incorrect assertion.
* g++.dg/cpp2a/concepts-requires27.C: New test.

gcc/cp/init.c
gcc/testsuite/g++.dg/cpp0x/pr78765.C
gcc/testsuite/g++.dg/cpp2a/concepts-requires27.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/sfinae14.C

index 88f6f90..d47e405 100644 (file)
@@ -39,8 +39,8 @@ along with GCC; see the file COPYING3.  If not see
 static bool begin_init_stmts (tree *, tree *);
 static tree finish_init_stmts (bool, tree, tree);
 static void construct_virtual_base (tree, tree);
-static void expand_aggr_init_1 (tree, tree, tree, tree, int, tsubst_flags_t);
-static void expand_default_init (tree, tree, tree, tree, int, tsubst_flags_t);
+static bool expand_aggr_init_1 (tree, tree, tree, tree, int, tsubst_flags_t);
+static bool expand_default_init (tree, tree, tree, tree, int, tsubst_flags_t);
 static void perform_member_init (tree, tree);
 static int member_init_ok_or_else (tree, tree, tree);
 static void expand_virtual_init (tree, tree);
@@ -1838,12 +1838,14 @@ build_aggr_init (tree exp, tree init, int flags, tsubst_flags_t complain)
   is_global = begin_init_stmts (&stmt_expr, &compound_stmt);
   destroy_temps = stmts_are_full_exprs_p ();
   current_stmt_tree ()->stmts_are_full_exprs_p = 0;
-  expand_aggr_init_1 (TYPE_BINFO (type), exp, exp,
-                     init, LOOKUP_NORMAL|flags, complain);
+  bool ok = expand_aggr_init_1 (TYPE_BINFO (type), exp, exp,
+                               init, LOOKUP_NORMAL|flags, complain);
   stmt_expr = finish_init_stmts (is_global, stmt_expr, compound_stmt);
   current_stmt_tree ()->stmts_are_full_exprs_p = destroy_temps;
   TREE_READONLY (exp) = was_const;
   TREE_THIS_VOLATILE (exp) = was_volatile;
+  if (!ok)
+    return error_mark_node;
 
   if ((VAR_P (exp) || TREE_CODE (exp) == PARM_DECL)
       && TREE_SIDE_EFFECTS (stmt_expr)
@@ -1854,7 +1856,7 @@ build_aggr_init (tree exp, tree init, int flags, tsubst_flags_t complain)
   return stmt_expr;
 }
 
-static void
+static bool
 expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
                      tsubst_flags_t complain)
 {
@@ -1889,6 +1891,9 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
        happen for direct-initialization, too.  */
     init = digest_init (type, init, complain);
 
+  if (init == error_mark_node)
+    return false;
+
   /* A CONSTRUCTOR of the target's type is a previously digested
      initializer, whether that happened just above or in
      cp_parser_late_parsing_nsdmi.
@@ -1910,7 +1915,7 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
       init = build2 (INIT_EXPR, TREE_TYPE (exp), exp, init);
       TREE_SIDE_EFFECTS (init) = 1;
       finish_expr_stmt (init);
-      return;
+      return true;
     }
 
   if (init && TREE_CODE (init) != TREE_LIST
@@ -1927,8 +1932,12 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
           have already built up the constructor call so we could wrap it
           in an exception region.  */;
       else
-       init = ocp_convert (type, init, CONV_IMPLICIT|CONV_FORCE_TEMP,
-                           flags, complain | tf_no_cleanup);
+       {
+         init = ocp_convert (type, init, CONV_IMPLICIT|CONV_FORCE_TEMP,
+                             flags, complain | tf_no_cleanup);
+         if (init == error_mark_node)
+           return false;
+       }
 
       if (TREE_CODE (init) == MUST_NOT_THROW_EXPR)
        /* We need to protect the initialization of a catch parm with a
@@ -1944,7 +1953,7 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
        init = build2 (INIT_EXPR, TREE_TYPE (exp), exp, init);
       TREE_SIDE_EFFECTS (init) = 1;
       finish_expr_stmt (init);
-      return;
+      return true;
     }
 
   if (init == NULL_TREE)
@@ -1982,6 +1991,8 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
                                        &parms, binfo, flags,
                                        complain);
       base = fold_build_cleanup_point_expr (void_type_node, base);
+      if (complete == error_mark_node || base == error_mark_node)
+       return false;
       rval = build_if_in_charge (complete, base);
     }
    else
@@ -1991,6 +2002,8 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
 
       rval = build_special_member_call (exp, ctor_name, &parms, binfo, flags,
                                        complain);
+      if (rval == error_mark_node)
+       return false;
     }
 
   if (parms != NULL)
@@ -2010,10 +2023,12 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
   /* FIXME put back convert_to_void?  */
   if (TREE_SIDE_EFFECTS (rval))
     finish_expr_stmt (rval);
+
+  return true;
 }
 
 /* This function is responsible for initializing EXP with INIT
-   (if any).
+   (if any).  Returns true on success, false on failure.
 
    BINFO is the binfo of the type for who we are performing the
    initialization.  For example, if W is a virtual base class of A and B,
@@ -2032,7 +2047,7 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
    FLAGS is just passed to `build_new_method_call'.  See that function
    for its description.  */
 
-static void
+static bool
 expand_aggr_init_1 (tree binfo, tree true_exp, tree exp, tree init, int flags,
                     tsubst_flags_t complain)
 {
@@ -2058,7 +2073,7 @@ expand_aggr_init_1 (tree binfo, tree true_exp, tree exp, tree init, int flags,
       if (init)
        finish_expr_stmt (init);
       gcc_assert (!cleanups);
-      return;
+      return true;
     }
 
   /* List-initialization from {} becomes value-initialization for non-aggregate
@@ -2096,7 +2111,7 @@ expand_aggr_init_1 (tree binfo, tree true_exp, tree exp, tree init, int flags,
       /* If we don't need to mess with the constructor at all,
         then we're done.  */
       if (! type_build_ctor_call (type))
-       return;
+       return true;
 
       /* Otherwise fall through and call the constructor.  */
       init = NULL_TREE;
@@ -2104,7 +2119,7 @@ expand_aggr_init_1 (tree binfo, tree true_exp, tree exp, tree init, int flags,
 
   /* We know that expand_default_init can handle everything we want
      at this point.  */
-  expand_default_init (binfo, true_exp, exp, init, flags, complain);
+  return expand_default_init (binfo, true_exp, exp, init, flags, complain);
 }
 
 /* Report an error if TYPE is not a user-defined, class type.  If
index 6b66d26..4c63fdd 100644 (file)
@@ -8,7 +8,7 @@ struct ValueType {
   int field;
 };
 
-static constexpr ValueType var = 0; // { dg-error "conversion" }
+static constexpr ValueType var = 0; // { dg-error "conversion|convert" }
 
 template <int> class ValueTypeInfo;
 
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires27.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires27.C
new file mode 100644 (file)
index 0000000..99d4500
--- /dev/null
@@ -0,0 +1,10 @@
+// PR c++/82110
+// { dg-do compile { target c++20 } }
+
+struct X { X() = delete; };
+
+template<class T> concept C = requires(T t) { new T; };
+template<class T> concept D = requires(T t) { new T[1]; };
+
+static_assert(!C<X>);
+static_assert(!D<X>);
index 93eba43..0c59dad 100644 (file)
@@ -76,4 +76,4 @@ STATIC_ASSERT(!(has_new_one_arg<X, int X::*>::value));
 
 STATIC_ASSERT((has_array_new<Y, int, 5>::value));
 STATIC_ASSERT(!(has_array_new<X, int Y::*, &Y::foo>::value));
-STATIC_ASSERT((has_array_new<X, int, 5>::value));
+STATIC_ASSERT(!(has_array_new<X, int, 5>::value));