]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: ill-formed constexpr function [PR113360]
authorJason Merrill <jason@redhat.com>
Tue, 31 Aug 2021 21:01:22 +0000 (17:01 -0400)
committerJason Merrill <jason@redhat.com>
Thu, 17 Apr 2025 02:25:45 +0000 (22:25 -0400)
If we already gave an error while parsing a function, we don't also need to
try to explain what's wrong with it when we later try to use it in a
constant-expression.  In the new testcase explain_invalid_constexpr_fn
couldn't find anything still in the function to complain about, so it said
because: followed by nothing.

We still try to constant-evaluate it to reduce error cascades, but we
shouldn't complain if it doesn't work very well.

This flag is similar to CLASSTYPE_ERRONEOUS that I added a while back.

PR c++/113360

gcc/cp/ChangeLog:

* cp-tree.h (struct language_function): Add erroneous bit.
* constexpr.cc (explain_invalid_constexpr_fn): Return if set.
(cxx_eval_call_expression): Quiet if set.
* parser.cc (cp_parser_function_definition_after_declarator)
* pt.cc (instantiate_body): Set it.

gcc/testsuite/ChangeLog:

* g++.dg/cpp23/constexpr-nonlit18.C: Remove redundant message.
* g++.dg/cpp1y/constexpr-diag2.C: New test.
* g++.dg/cpp1y/pr63996.C: Adjust expected errors.
* g++.dg/template/explicit-args6.C: Likewise.
* g++.dg/cpp0x/constexpr-ice21.C: Likewise.

gcc/cp/constexpr.cc
gcc/cp/cp-tree.h
gcc/cp/parser.cc
gcc/cp/pt.cc
gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C
gcc/testsuite/g++.dg/cpp1y/constexpr-diag2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/pr63996.C
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit18.C
gcc/testsuite/g++.dg/template/explicit-args6.C

index 4346b29abc685e0102e4624d42dbef4c2f1cb6f7..d647a09269d3a6506832a6a7a2c14b69ac74c8e1 100644 (file)
@@ -1048,6 +1048,12 @@ explain_invalid_constexpr_fn (tree fun)
 {
   static hash_set<tree> *diagnosed;
   tree body;
+
+  /* Don't try to explain a function we already complained about.  */
+  if (function *f = DECL_STRUCT_FUNCTION (fun))
+    if (f->language->erroneous)
+      return;
+
   /* In C++23, a function marked 'constexpr' may not actually be a constant
      expression.  We haven't diagnosed the problem yet: -Winvalid-constexpr
      wasn't enabled.  The function was called, so diagnose why it cannot be
@@ -3079,6 +3085,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
     }
 
   constexpr_ctx new_ctx = *ctx;
+  ctx = &new_ctx;
   if (DECL_CONSTRUCTOR_P (fun) && !ctx->object
       && TREE_CODE (t) == AGGR_INIT_EXPR)
     {
@@ -3088,16 +3095,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
       tree ctor = new_ctx.ctor = build_constructor (DECL_CONTEXT (fun), NULL);
       CONSTRUCTOR_NO_CLEARING (ctor) = true;
       ctx->global->put_value (new_ctx.object, ctor);
-      ctx = &new_ctx;
     }
   /* An immediate invocation is manifestly constant evaluated including the
      arguments of the call, so use mce_true even for the argument
      evaluation.  */
   if (DECL_IMMEDIATE_FUNCTION_P (fun))
-    {
-      new_ctx.manifestly_const_eval = mce_true;
-      ctx = &new_ctx;
-    }
+    new_ctx.manifestly_const_eval = mce_true;
 
   /* We used to shortcut trivial constructor/op= here, but nowadays
      we can only get a trivial function here with -fno-elide-constructors.  */
@@ -3185,6 +3188,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
         }
     }
 
+  /* Don't complain about problems evaluating an ill-formed function.  */
+  if (function *f = DECL_STRUCT_FUNCTION (fun))
+    if (f->language->erroneous)
+      new_ctx.quiet = true;
+
   int depth_ok = push_cx_call_context (t);
 
   /* Remember the object we are constructing or destructing.  */
index 55f986e25c14da1e9a315da72505e4716d18d48f..7798efba3dbad6daa4327655b07a0308ca18e12b 100644 (file)
@@ -2206,6 +2206,8 @@ struct GTY(()) language_function {
 
   BOOL_BITFIELD invalid_constexpr : 1;
   BOOL_BITFIELD throwing_cleanup : 1;
+  /* True if we gave any errors in this function.  */
+  BOOL_BITFIELD erroneous : 1;
 
   hash_table<named_label_hash> *x_named_labels;
 
index 812a7c5ae7d9c6bbfb5a1afcb78303c6caf8ff93..3628cfefa07c835868587226221c659182796332 100644 (file)
@@ -33634,6 +33634,8 @@ cp_parser_function_definition_after_declarator (cp_parser* parser,
     = parser->num_template_parameter_lists;
   parser->num_template_parameter_lists = 0;
 
+  int errs = errorcount + sorrycount;
+
   /* If the next token is `try', `__transaction_atomic', or
      `__transaction_relaxed`, then we are looking at either function-try-block
      or function-transaction-block.  Note that all of these include the
@@ -33653,6 +33655,9 @@ cp_parser_function_definition_after_declarator (cp_parser* parser,
   fn = finish_function (inline_p);
   check_module_decl_linkage (fn);
 
+  if ((errorcount + sorrycount) > errs)
+    DECL_STRUCT_FUNCTION (fn)->language->erroneous = true;
+
   if (modules_p ()
       && !inline_p
       && TYPE_P (DECL_CONTEXT (fn))
index 51433e7c4ec55e0dea639455ec9c7b430c9f8c68..a71705fd085b5f33479d4cac539c7ba1b32965d7 100644 (file)
@@ -27758,6 +27758,11 @@ instantiate_body (tree pattern, tree args, tree d, bool nested_p)
 
       if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern))
        cp_check_omp_declare_reduction (d);
+
+      if (int errs = errorcount + sorrycount)
+       if (errs > current_tinst_level->errors)
+         if (function *f = DECL_STRUCT_FUNCTION (d))
+           f->language->erroneous = true;
     }
 
   /* We're not deferring instantiation any more.  */
index 46273654f240e4283b4381f0973e1285b935fe72..dcc404489bec9a7fe801bb64c5653a0916ce2fb1 100644 (file)
@@ -3,7 +3,7 @@
 
 struct NoMut1 { int a, b; };
 struct NoMut3 : virtual NoMut1 {
-  constexpr NoMut3(int a, int b) // { dg-error "virtual base" "" { target c++23 } }
+  constexpr NoMut3(int a, int b)
     : NoMut1{a, b}
   {} // { dg-error "virtual base" }
 };
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-diag2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-diag2.C
new file mode 100644 (file)
index 0000000..93f3f10
--- /dev/null
@@ -0,0 +1,12 @@
+// PR c++/113360
+// { dg-do compile { target c++14 } }
+
+constexpr bool init_list()     // { dg-bogus "because" }
+{
+  int total{};
+  for (int x : {1, 2, 3})      // { dg-error "initializer list" }
+    total += x;
+  return total == 6;
+}
+
+static_assert(init_list(), "");        // { dg-error "constant" }
index 8eee2e0af3090c013921a003e50a37435ae26861..347c86cc63cece75f05c6804b88ea288f47734ef 100644 (file)
@@ -1,5 +1,4 @@
 // { dg-do compile { target c++14 } }
-// { dg-additional-options "-Wno-return-type" }
 
 constexpr int
 foo (int i)
@@ -8,4 +7,4 @@ foo (int i)
   if (i == 23) return 0;
 }
 
-constexpr int j = foo (1); // { dg-error "flows off the end|in .constexpr. expansion of" }
+constexpr int j = foo (1);
index 8e230ef3bc3ab4c6809e59f28e4c52a628eddbe3..f8918148cda1ba5a4a4f6e157dfbe94f2312bdbe 100644 (file)
@@ -24,7 +24,7 @@ f3 ()
 }
 
 constexpr int
-f4 ()                                  // { dg-message "declared here" "" { target c++20_down } }
+f4 ()
 {                                      // { dg-message "is not usable as a 'constexpr' function because:" "" { target c++23 } .-1 }
   static const int a = f1 (1);         // { dg-error "'a' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } }
   return 0;                            // { dg-error "'a' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 }
index 18663d7bcf9516ff01239906240c2d85dff4000c..0d9718c38f6bf57c3d26b77e04f2d6a7b3573332 100644 (file)
@@ -24,10 +24,12 @@ frob()
 
   // narrowing check, reject negative values
   return unsigned{N};          // { dg-prune-output "narrowing" }
-} // { dg-prune-output "flows off the end" }
-// { dg-prune-output "not a return-statement" }
+}
 
-template<int N> void get_n(tuple& t) { get<frob<N>()>(t); } // { dg-error "" }
+// This complains about calling frob only in C++11 because
+// maybe_save_constexpr_fundef fails; in later standards it succeeds,
+// and the evaluation failure is silent due to the earlier errors.
+template<int N> void get_n(tuple& t) { get<frob<N>()>(t); } // { dg-error "" "" { target c++11_only } }
 
 int main()
 {