This patch deals with constexpr functions and whether or not they should be
noexcept.

CWG 1129 specified that constexpr functions are noexcept: it was a special case
in [expr.unary.noexcept].  This was accidentally removed in <wg21.link/p0003>
but the CWG conclusion was to keep it as-is.

Clearly we need to change this for C++17.  The question is whether we should
treat it as a DR and apply it retroactively (which is what clang did).  I took
the same approach and my reasoning is in the comment in check_noexcept_r.

Arguably it might be too late to put this in now, maybe we should defer to GCC 
10.
But at least there's a patch.

Bootstrapped/regtested on x86_64-linux.

2019-04-11  Marek Polacek  <pola...@redhat.com>

        PR c++/87603 - constexpr functions are no longer noexcept.
        * constexpr.c (is_sub_constant_expr): Remove unused function.
        * cp-tree.h (is_sub_constant_expr): Remove declaration.
        * except.c (check_noexcept_r): Don't consider a call to a constexpr
        function noexcept.

        * g++.dg/cpp0x/constexpr-noexcept.C: Adjust the expected result.
        * g++.dg/cpp0x/constexpr-noexcept3.C: Likewise.
        * g++.dg/cpp0x/constexpr-noexcept4.C: Likewise.
        * g++.dg/cpp0x/constexpr-noexcept8.C: New test.
        * g++.dg/cpp0x/inh-ctor32.C: Remove dg-message.
        * g++.dg/cpp1y/constexpr-noexcept1.C: New test.

diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 0ce5618a2fc..9c13f0d5f32 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -5423,27 +5423,6 @@ cxx_eval_outermost_constant_expr (tree t, bool 
allow_non_constant,
   return r;
 }
 
-/* Returns true if T is a valid subexpression of a constant expression,
-   even if it isn't itself a constant expression.  */
-
-bool
-is_sub_constant_expr (tree t)
-{
-  bool non_constant_p = false;
-  bool overflow_p = false;
-  hash_map <tree, tree> map;
-  HOST_WIDE_INT constexpr_ops_count = 0;
-
-  constexpr_ctx ctx
-    = { NULL, &map, NULL, NULL, NULL, NULL, &constexpr_ops_count,
-       true, true, false };
-
-  instantiate_constexpr_fns (t);
-  cxx_eval_constant_expression (&ctx, t, false, &non_constant_p,
-                               &overflow_p);
-  return !non_constant_p && !overflow_p;
-}
-
 /* If T represents a constant expression returns its reduced value.
    Otherwise return error_mark_node.  If T is dependent, then
    return NULL.  */
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index b87b968fa4e..ff4ce068a83 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -7720,7 +7720,6 @@ extern tree fold_non_dependent_init               (tree,
                                                 tsubst_flags_t = 
tf_warning_or_error,
                                                 bool = false);
 extern tree fold_simple                                (tree);
-extern bool is_sub_constant_expr                (tree);
 extern bool reduced_constant_expression_p       (tree);
 extern bool is_instantiation_of_constexpr       (tree);
 extern bool var_in_constexpr_fn                 (tree);
diff --git gcc/cp/except.c gcc/cp/except.c
index 40e973fad66..7df17c52eb4 100644
--- gcc/cp/except.c
+++ gcc/cp/except.c
@@ -1128,11 +1128,14 @@ check_noexcept_r (tree *tp, int * /*walk_subtrees*/, 
void * /*data*/)
              && (DECL_ARTIFICIAL (fn)
                  || nothrow_libfn_p (fn)))
            return TREE_NOTHROW (fn) ? NULL_TREE : fn;
-         /* A call to a constexpr function is noexcept if the call
-            is a constant expression.  */
-         if (DECL_DECLARED_CONSTEXPR_P (fn)
-             && is_sub_constant_expr (t))
-           return NULL_TREE;
+         /* We used to treat a call to a constexpr function as noexcept if
+            the call was a constant expression (CWG 1129).  This has changed
+            in P0003 whereby noexcept has no special rule for constant
+            expressions anymore.  Since the current behavior is important for
+            certain library functionality, we treat this as a DR, therefore
+            adjusting the behavior for C++11 and C++14.  Previously, we had
+            to evaluate the noexcept-specifier's operand here, but that could
+            cause instantiations that would fail.  */
        }
       if (!TYPE_NOTHROW_P (type))
        return fn;
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept.C 
gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept.C
index dbadaa8e3d2..035afd13d39 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept.C
@@ -10,4 +10,7 @@ constexpr T value(T t) noexcept(is_funny<T>::value) { return 
t; } // Line 7
 
 constexpr bool ok = noexcept(value(42));
 
-static_assert(ok, "Assertion failure");
+// We used to treat a call to a constexpr function as noexcept if
+// the call was a constant expression.  We no longer do since
+// c++/87603.
+static_assert(!ok, "Assertion failure");
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept3.C 
gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept3.C
index 9541bc0781d..5a43899b5fa 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept3.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept3.C
@@ -2,6 +2,8 @@
 
 constexpr int f(int i) { return i; }
 #define SA(X) static_assert (X, #X)
-SA(noexcept(f(42)));
+/* We used to assert that the following *is* noexcept, but this has changed
+   in c++/87603.  */
+SA(!noexcept(f(42)));
 int j;
 SA(!noexcept(f(j)));
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept4.C 
gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept4.C
index eb7190023ed..0446069107d 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept4.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept4.C
@@ -1,6 +1,7 @@
 // { dg-do compile { target c++11 } }
-// A call is noexcept if it is a valid subexpression of a constant
-// expression, even if it is not itself a constant expression.
+// We used to treat a call to a constexpr function as noexcept if
+// the call was a constant expression.  We no longer do since
+// c++/87603.
 
 #define SA(X) static_assert(X,#X)
 
@@ -9,6 +10,6 @@ constexpr const int* f(const int *p) { return p; }
 int main()
 {
   constexpr int i = 42;
-  SA(noexcept(*f(&i)));
-  SA(noexcept(f(&i)));
+  SA(!noexcept(*f(&i)));
+  SA(!noexcept(f(&i)));
 }
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept8.C 
gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept8.C
new file mode 100644
index 00000000000..7dca56ae23c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-noexcept8.C
@@ -0,0 +1,10 @@
+// PR c++/87603
+// { dg-do compile { target c++11 } }
+
+struct Y { };
+
+          bool operator<(Y a, Y b) { return false; }
+constexpr bool operator>(Y a, Y b) { return false; }
+
+static_assert(!noexcept(Y{} > Y{}), "");
+static_assert(!noexcept(Y{} < Y{}), "");
diff --git gcc/testsuite/g++.dg/cpp0x/inh-ctor32.C 
gcc/testsuite/g++.dg/cpp0x/inh-ctor32.C
index c40412fc534..5ea2642e415 100644
--- gcc/testsuite/g++.dg/cpp0x/inh-ctor32.C
+++ gcc/testsuite/g++.dg/cpp0x/inh-ctor32.C
@@ -168,7 +168,7 @@ namespace derived_ctor {
       };
       struct bar : boo {
        template <typename ...T>
-       constexpr bar(T ... args) : boo(args...) {} // { dg-message "sorry, 
unimplemented: passing arguments to ellipsis" }
+       constexpr bar(T ... args) : boo(args...) {}
       };
       void f() noexcept(noexcept(bar{0,1}));
     }
@@ -200,12 +200,12 @@ namespace derived_ctor {
       };
       struct bor : boo {
        template <typename ...T>
-       constexpr bor(T ... args) : boo(args...) {} // { dg-message "sorry, 
unimplemented: passing arguments to ellipsis" }
+       constexpr bor(T ... args) : boo(args...) {}
       };
       struct bar : bor {
        using bor::bor;
       };
-      void f() noexcept(noexcept(bar{0,1})); // { dg-message "'constexpr' 
expansion" }
+      void f() noexcept(noexcept(bar{0,1}));
     }
 
     namespace no_constexpr_noninherited_ctor {
diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-noexcept1.C 
gcc/testsuite/g++.dg/cpp1y/constexpr-noexcept1.C
new file mode 100644
index 00000000000..a74f24a3b30
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-noexcept1.C
@@ -0,0 +1,12 @@
+// PR c++/87603
+// { dg-do compile { target c++14 } }
+
+template<typename T>
+struct basic_string_view
+{
+  constexpr basic_string_view(T p) noexcept { (void) p.i; }
+};
+
+struct X { } x;
+
+bool b = noexcept(basic_string_view<X>{x});

Reply via email to