Implement P1286R2, Contra CWG1778
authorJason Merrill <jason@redhat.com>
Wed, 23 Oct 2019 20:41:26 +0000 (16:41 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Wed, 23 Oct 2019 20:41:26 +0000 (16:41 -0400)
The C++11 requirement that an explicit exception-specification on a
defaulted function match the implicit one was found to be problematic for
std::atomic.  This paper, adopted in February, simply removes that
requirement: if an explicitly defaulted function has a different
exception-specification, that now works just like a user-written function:
either it isn't noexcept when it could be, or it is noexcept and will call
terminate if an exception is thrown.

* method.c (defaulted_late_check): Don't check explicit
exception-specification on defaulted function.
(after_nsdmi_defaulted_late_checks): Remove.
* parser.h (struct cp_unparsed_functions_entry): Remove classes.
* parser.c (unparsed_classes): Remove.
(push_unparsed_function_queues, cp_parser_class_specifier_1):
Adjust.

From-SVN: r277351

gcc/cp/ChangeLog
gcc/cp/method.c
gcc/cp/parser.c
gcc/cp/parser.h
gcc/testsuite/g++.dg/DRs/dr1778.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/defaulted23.C
gcc/testsuite/g++.dg/cpp0x/defaulted43.C

index d1518123b2481ced6589390798f73b9185b4e9af..f587f15ddd7a0c824f66de80e3bec46bbf612ba0 100644 (file)
@@ -1,3 +1,14 @@
+2019-10-23  Jason Merrill  <jason@redhat.com>
+
+       Implement P1286R2, Contra CWG1778
+       * method.c (defaulted_late_check): Don't check explicit
+       exception-specification on defaulted function.
+       (after_nsdmi_defaulted_late_checks): Remove.
+       * parser.h (struct cp_unparsed_functions_entry): Remove classes.
+       * parser.c (unparsed_classes): Remove.
+       (push_unparsed_function_queues, cp_parser_class_specifier_1):
+       Adjust.
+
 2019-10-23  Jakub Jelinek  <jakub@redhat.com>
 
        * constexpr.c (cxx_eval_constant_expression) <case CLEANUP_STMT>:
index 73a01147ff9b0193d2d80dd63347ef774c66846f..b613e5df871bb79ec5ce9badf366d261dff1156a 100644 (file)
@@ -2204,40 +2204,12 @@ defaulted_late_check (tree fn)
       return;
     }
 
-  /* 8.4.2/2: An explicitly-defaulted function (...) may have an explicit
-     exception-specification only if it is compatible (15.4) with the 
-     exception-specification on the implicit declaration.  If a function
-     is explicitly defaulted on its first declaration, (...) it is
-     implicitly considered to have the same exception-specification as if
-     it had been implicitly declared.  */
-  maybe_instantiate_noexcept (fn);
-  tree fn_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
-  if (!fn_spec)
-    {
-      if (DECL_DEFAULTED_IN_CLASS_P (fn))
-       TREE_TYPE (fn) = build_exception_variant (TREE_TYPE (fn), eh_spec);
-    }
-  else if (UNEVALUATED_NOEXCEPT_SPEC_P (fn_spec))
-    /* Equivalent to the implicit spec.  */;
-  else if (DECL_DEFAULTED_IN_CLASS_P (fn)
-          && !CLASSTYPE_TEMPLATE_INSTANTIATION (ctx))
-    /* We can't compare an explicit exception-specification on a
-       constructor defaulted in the class body to the implicit
-       exception-specification until after we've parsed any NSDMI; see
-       after_nsdmi_defaulted_late_checks.  */;
-  else
-    {
-      tree eh_spec = get_defaulted_eh_spec (fn);
-      if (!comp_except_specs (fn_spec, eh_spec, ce_normal))
-       {
-         if (DECL_DEFAULTED_IN_CLASS_P (fn))
-           DECL_DELETED_FN (fn) = true;
-         else
-           error ("function %q+D defaulted on its redeclaration "
-                  "with an exception-specification that differs from "
-                  "the implicit exception-specification %qX", fn, eh_spec);
-       }
-    }
+  /* If a function is explicitly defaulted on its first declaration without an
+     exception-specification, it is implicitly considered to have the same
+     exception-specification as if it had been implicitly declared.  */
+  if (!TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn))
+      && DECL_DEFAULTED_IN_CLASS_P (fn))
+    TREE_TYPE (fn) = build_exception_variant (TREE_TYPE (fn), eh_spec);
 
   if (DECL_DEFAULTED_IN_CLASS_P (fn)
       && DECL_DECLARED_CONSTEXPR_P (implicit_fn))
@@ -2264,35 +2236,6 @@ defaulted_late_check (tree fn)
     }
 }
 
-/* OK, we've parsed the NSDMI for class T, now we can check any explicit
-   exception-specifications on functions defaulted in the class body.  */
-
-void
-after_nsdmi_defaulted_late_checks (tree t)
-{
-  if (uses_template_parms (t))
-    return;
-  if (t == error_mark_node)
-    return;
-  for (tree fn = TYPE_FIELDS (t); fn; fn = DECL_CHAIN (fn))
-    if (!DECL_ARTIFICIAL (fn)
-       && DECL_DECLARES_FUNCTION_P (fn)
-       && DECL_DEFAULTED_IN_CLASS_P (fn))
-      {
-       tree fn_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
-       if (UNEVALUATED_NOEXCEPT_SPEC_P (fn_spec))
-         continue;
-
-       tree eh_spec = get_defaulted_eh_spec (fn);
-       if (eh_spec == error_mark_node)
-         continue;
-
-       if (!comp_except_specs (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)),
-                               eh_spec, ce_normal))
-         DECL_DELETED_FN (fn) = true;
-      }
-}
-
 /* Returns true iff FN can be explicitly defaulted, and gives any
    errors if defaulting FN is ill-formed.  */
 
index c597adb38f193251c28d5f9e62e0510a369a1476..3857fe47d6781ae6865a48dd385d11042051b5fc 100644 (file)
@@ -2005,16 +2005,13 @@ cp_parser_context_new (cp_parser_context* next)
   parser->unparsed_queues->last ().funs_with_definitions
 #define unparsed_nsdmis \
   parser->unparsed_queues->last ().nsdmis
-#define unparsed_classes \
-  parser->unparsed_queues->last ().classes
 #define unparsed_noexcepts \
   parser->unparsed_queues->last ().noexcepts
 
 static void
 push_unparsed_function_queues (cp_parser *parser)
 {
-  cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL,
-                                   NULL };
+  cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL };
   vec_safe_push (parser->unparsed_queues, e);
 }
 
@@ -23754,7 +23751,6 @@ cp_parser_class_specifier_1 (cp_parser* parser)
             error recovery (c++/71169, c++/71832).  */
          vec_safe_truncate (unparsed_funs_with_default_args, 0);
          vec_safe_truncate (unparsed_nsdmis, 0);
-         vec_safe_truncate (unparsed_classes, 0);
          vec_safe_truncate (unparsed_funs_with_definitions, 0);
        }
 
@@ -23809,12 +23805,6 @@ cp_parser_class_specifier_1 (cp_parser* parser)
       if (pushed_scope)
        pop_scope (pushed_scope);
 
-      /* Now do some post-NSDMI bookkeeping.  */
-      FOR_EACH_VEC_SAFE_ELT (unparsed_classes, ix, class_type)
-       after_nsdmi_defaulted_late_checks (class_type);
-      vec_safe_truncate (unparsed_classes, 0);
-      after_nsdmi_defaulted_late_checks (type);
-
       /* If there are noexcept-specifiers that have not yet been processed,
         take care of them now.  */
       class_type = NULL_TREE;
@@ -23885,8 +23875,6 @@ cp_parser_class_specifier_1 (cp_parser* parser)
          cp_parser_late_parsing_for_member (parser, decl);
       vec_safe_truncate (unparsed_funs_with_definitions, 0);
     }
-  else
-    vec_safe_push (unparsed_classes, type);
 
   /* Put back any saved access checks.  */
   pop_deferring_access_checks ();
index 91b5916622d950195b81fad610da283273dfd452..200498281b57720ea00633fbd54538212f3b03e4 100644 (file)
@@ -163,10 +163,6 @@ struct GTY(()) cp_unparsed_functions_entry {
      FIELD_DECLs appear in this list in declaration order.  */
   vec<tree, va_gc> *nsdmis;
 
-  /* Nested classes go in this vector, so that we can do some final
-     processing after parsing any NSDMIs.  */
-  vec<tree, va_gc> *classes;
-
   /* Functions with noexcept-specifiers that require post-processing.  */
   vec<tree, va_gc> *noexcepts;
 };
diff --git a/gcc/testsuite/g++.dg/DRs/dr1778.C b/gcc/testsuite/g++.dg/DRs/dr1778.C
new file mode 100644 (file)
index 0000000..8db937f
--- /dev/null
@@ -0,0 +1,7 @@
+// P1286R2: Contra CWG1778
+// { dg-do compile { target c++11 } }
+
+struct T { T(); T(T &&) noexcept(false); };
+struct U { T t; U(); U(U &&) noexcept = default; };
+U u1;
+U u2 = static_cast<U&&>(u1);      // OK, calls std::terminate if T::T(T&&) throws
index dfbdd2f2ed1bca53af5e1fabb737fa3f0a5a51f0..23848633c3cc22f0569ac195524db1af25cfbbe8 100644 (file)
@@ -10,10 +10,10 @@ A a;
 
 struct B
 {
-  B() throw (int) = default; // { dg-message "exception-specification" "" { target { ! c++17 } } }
+  B() throw (int) = default;
 };                             // { dg-error "dynamic exception specification" "" { target c++17 } .-1 }
                                // { dg-warning "deprecated" "" { target { ! c++17 } } .-2 }
-B b;                           // { dg-error "deleted" "" { target { ! c++17 } } }
+B b;
 
 struct C
 {
index f2846fe390c7b07987c555dfff6a3f7900e30b2d..1fe7818ec677f2d54cbce787bed7cfce0f4b215f 100644 (file)
@@ -17,8 +17,8 @@ struct A
   T t;
 };
 
-A::A() noexcept = default;   // { dg-error "defaulted" }
-A::~A() noexcept = default;  // { dg-error "defaulted" }
+A::A() noexcept = default;
+A::~A() noexcept = default;
 
 struct U
 {
@@ -51,10 +51,10 @@ V v;
 
 struct C
 {
-  C() noexcept = default;      // { dg-message "exception-specification" }
-  ~C() noexcept = default;     // { dg-message "exception-specification" }
+  C() noexcept = default;
+  ~C() noexcept = default;
 
   V v;
 };
 
-C c;                           // { dg-error "deleted" }
+C c;