c++: Use error_at rather than warning_at for missing return in constexpr functions...
authorJakub Jelinek <jakub@redhat.com>
Fri, 31 Jul 2020 21:08:00 +0000 (23:08 +0200)
committerJakub Jelinek <jakub@redhat.com>
Fri, 31 Jul 2020 21:08:00 +0000 (23:08 +0200)
For C++11 we already emit an error if a constexpr function doesn't contain
a return statement, because in C++11 that is the only thing it needs to
contain, but for C++14 we would normally issue a -Wreturn-type warning.

As mentioned by Jonathan, such constexpr functions are invalid, no
diagnostics required, because there doesn't exist any arguments for
which it would result in valid constant expression.

This raises it to an error in such cases.  The !LAMBDA_TYPE_P case
is to avoid error on g++.dg/pr81194.C where the user didn't write
constexpr anywhere and the operator() is compiler generated.

2020-07-31  Jakub Jelinek  <jakub@redhat.com>

PR c++/96182
* decl.c (finish_function): In constexpr functions use for C++14 and
later error instead of warning if no return statement is present and
diagnose it regardless of warn_return_type.  Move the warn_return_type
diagnostics earlier in the function.

* g++.dg/cpp1y/constexpr-96182.C: New test.
* g++.dg/other/error35.C (S<T>::g()): Add return statement.
* g++.dg/cpp1y/pr63996.C (foo): Likewise.
* g++.dg/cpp1y/constexpr-return2.C (f): Likewise.
* g++.dg/cpp1y/var-templ44.C (make_array): Add throw 1.

gcc/cp/decl.c
gcc/testsuite/g++.dg/cpp1y/constexpr-96182.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/constexpr-return2.C
gcc/testsuite/g++.dg/cpp1y/pr63996.C
gcc/testsuite/g++.dg/cpp1y/var-templ44.C
gcc/testsuite/g++.dg/other/error35.C

index b4beaa94257bc1818dae6f72ea95757d21adc0d4..a68bbe04da7e105a79e20110ffca4df599212e7d 100644 (file)
@@ -17112,6 +17112,51 @@ finish_function (bool inline_p)
                                      DECL_ATTRIBUTES (fndecl)))
       omp_declare_variant_finalize (fndecl, attr);
 
+  /* Complain if there's just no return statement.  */
+  if ((warn_return_type
+       || (cxx_dialect >= cxx14
+          && DECL_DECLARED_CONSTEXPR_P (fndecl)))
+      && !VOID_TYPE_P (TREE_TYPE (fntype))
+      && !dependent_type_p (TREE_TYPE (fntype))
+      && !current_function_returns_value && !current_function_returns_null
+      /* Don't complain if we abort or throw.  */
+      && !current_function_returns_abnormally
+      /* Don't complain if there's an infinite loop.  */
+      && !current_function_infinite_loop
+      /* Don't complain if we are declared noreturn.  */
+      && !TREE_THIS_VOLATILE (fndecl)
+      && !DECL_NAME (DECL_RESULT (fndecl))
+      && !TREE_NO_WARNING (fndecl)
+      /* Structor return values (if any) are set by the compiler.  */
+      && !DECL_CONSTRUCTOR_P (fndecl)
+      && !DECL_DESTRUCTOR_P (fndecl)
+      && targetm.warn_func_return (fndecl))
+    {
+      gcc_rich_location richloc (input_location);
+      /* Potentially add a "return *this;" fix-it hint for
+        assignment operators.  */
+      if (IDENTIFIER_ASSIGN_OP_P (DECL_NAME (fndecl)))
+       {
+         tree valtype = TREE_TYPE (DECL_RESULT (fndecl));
+         if (TREE_CODE (valtype) == REFERENCE_TYPE
+             && current_class_ref
+             && same_type_ignoring_top_level_qualifiers_p
+                 (TREE_TYPE (valtype), TREE_TYPE (current_class_ref))
+             && global_dc->option_enabled (OPT_Wreturn_type,
+                                           global_dc->lang_mask,
+                                           global_dc->option_state))
+           add_return_star_this_fixit (&richloc, fndecl);
+       }
+      if (cxx_dialect >= cxx14
+         && DECL_DECLARED_CONSTEXPR_P (fndecl))
+       error_at (&richloc, "no return statement in %<constexpr%> function "
+                           "returning non-void");
+      else if (warning_at (&richloc, OPT_Wreturn_type,
+                          "no return statement in function returning "
+                          "non-void"))
+       TREE_NO_WARNING (fndecl) = 1;
+    }
+
   /* Lambda closure members are implicitly constexpr if possible.  */
   if (cxx_dialect >= cxx17
       && LAMBDA_TYPE_P (CP_DECL_CONTEXT (fndecl)))
@@ -17163,44 +17208,6 @@ finish_function (bool inline_p)
      to the FUNCTION_DECL node itself.  */
   BLOCK_SUPERCONTEXT (DECL_INITIAL (fndecl)) = fndecl;
 
-  /* Complain if there's just no return statement.  */
-  if (warn_return_type
-      && !VOID_TYPE_P (TREE_TYPE (fntype))
-      && !dependent_type_p (TREE_TYPE (fntype))
-      && !current_function_returns_value && !current_function_returns_null
-      /* Don't complain if we abort or throw.  */
-      && !current_function_returns_abnormally
-      /* Don't complain if there's an infinite loop.  */
-      && !current_function_infinite_loop
-      /* Don't complain if we are declared noreturn.  */
-      && !TREE_THIS_VOLATILE (fndecl)
-      && !DECL_NAME (DECL_RESULT (fndecl))
-      && !TREE_NO_WARNING (fndecl)
-      /* Structor return values (if any) are set by the compiler.  */
-      && !DECL_CONSTRUCTOR_P (fndecl)
-      && !DECL_DESTRUCTOR_P (fndecl)
-      && targetm.warn_func_return (fndecl))
-    {
-      gcc_rich_location richloc (input_location);
-      /* Potentially add a "return *this;" fix-it hint for
-        assignment operators.  */
-      if (IDENTIFIER_ASSIGN_OP_P (DECL_NAME (fndecl)))
-       {
-         tree valtype = TREE_TYPE (DECL_RESULT (fndecl));
-         if (TREE_CODE (valtype) == REFERENCE_TYPE
-             && current_class_ref
-             && same_type_ignoring_top_level_qualifiers_p
-                 (TREE_TYPE (valtype), TREE_TYPE (current_class_ref))
-             && global_dc->option_enabled (OPT_Wreturn_type,
-                                           global_dc->lang_mask,
-                                           global_dc->option_state))
-           add_return_star_this_fixit (&richloc, fndecl);
-       }
-      if (warning_at (&richloc, OPT_Wreturn_type,
-         "no return statement in function returning non-void"))
-       TREE_NO_WARNING (fndecl) = 1;
-    }
-
   /* Store the end of the function, so that we get good line number
      info for the epilogue.  */
   cfun->function_end_locus = input_location;
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-96182.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-96182.C
new file mode 100644 (file)
index 0000000..9c07d71
--- /dev/null
@@ -0,0 +1,6 @@
+// PR c++/96182
+// { dg-do compile { target c++11 } }
+
+constexpr int foo () {} // { dg-error "no return statement in 'constexpr' function returning non-void" "" { target c++14 } }
+// { dg-error "body of 'constexpr' function 'constexpr int foo\\\(\\\)' not a return-statement" "" { target c++11_only } .-1 }
+// { dg-warning "no return statement in function returning non-void" "" { target c++11_only } .-2 }
index cb0185414afedc332241cb206afd6c05f699f478..3357fe3cf23c6bc7103859e826a176ed7dd347b4 100644 (file)
@@ -3,6 +3,7 @@
 
 constexpr int f (int i)
 {
+  if (i == -1) return 0;
 }
 
 constexpr int i = f(42);       // { dg-error "flows off the end|in .constexpr. expansion of " }
index fe47544a3c87abbbaed9f8592d508ae230dc95d7..8eee2e0af3090c013921a003e50a37435ae26861 100644 (file)
@@ -5,6 +5,7 @@ constexpr int
 foo (int i)
 {
   int a[i] = { }; // { dg-error "7:ISO C\\+\\+ forbids variable length array .a" }
+  if (i == 23) return 0;
 }
 
 constexpr int j = foo (1); // { dg-error "flows off the end|in .constexpr. expansion of" }
index 2ef01cf74806d43b428e23bef2675f46c94006a1..74954ed102c99221cb8e281bbde815eaa5d6909c 100644 (file)
@@ -26,5 +26,6 @@ constexpr auto make_array()
     -> array<conditional_t<is_void_v<_Dest>, common_type_t<>, _Dest>,
              sizeof...(_Types)> {
   static_assert(__or_<__not_<is_void<_Dest>>, __and_<>>::value, ""); // { dg-error "static assert" }
+  throw 1;
 }
 auto d = make_array();
index e9c8371f18b6f7d7f344113117106222b70c693b..063bc4a9680e3eb332716e02e5c2eba33107cb23 100644 (file)
@@ -9,6 +9,6 @@ template <typename> struct S {
 enum S<char>::E;
 template <typename T> enum S<T>::E : int { b };
 template <typename T>
-constexpr int S<T>::g() const { b; } // { dg-error "not declared" }
+constexpr int S<T>::g() const { b; if (false) return 0; } // { dg-error "not declared" }
 static_assert(S<char>().g() == 1, ""); // { dg-error "" }
 // { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }