On Wed, 16 Apr 2025, Jason Merrill wrote:

> Tested x86_64-pc-linux-gnu, applying to trunk.
> 
> -- 8< --
> 
> 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/cp-tree.h                               |  2 ++
>  gcc/cp/constexpr.cc                            | 18 +++++++++++++-----
>  gcc/cp/parser.cc                               |  5 +++++
>  gcc/cp/pt.cc                                   |  5 +++++
>  gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C   |  2 +-
>  gcc/testsuite/g++.dg/cpp1y/constexpr-diag2.C   | 12 ++++++++++++
>  gcc/testsuite/g++.dg/cpp1y/pr63996.C           |  3 +--
>  .../g++.dg/cpp23/constexpr-nonlit18.C          |  2 +-
>  gcc/testsuite/g++.dg/template/explicit-args6.C |  8 +++++---
>  9 files changed, 45 insertions(+), 12 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-diag2.C
> 
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 55f986e25c1..7798efba3db 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -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;
>  
> diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
> index 4346b29abc6..d647a09269d 100644
> --- a/gcc/cp/constexpr.cc
> +++ b/gcc/cp/constexpr.cc
> @@ -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.  */
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 812a7c5ae7d..3628cfefa07 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -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;
> +

IIUC this means for

  template<class T> struct A { using type = T::type; };

  void f() {
    A<int> a;
  }

  void g() {
    [] { A<char> a; };
  }

we'll mark A<int>, f, g and the lambda as erroneous, which doesn't seem
ideal (since e.g. A<int> might still be "usable enough" despite its one
member being erroneous), but it's probably the best we can do without
making the mechanism a lot more complex.

>    if (modules_p ()
>        && !inline_p
>        && TYPE_P (DECL_CONTEXT (fn))
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 51433e7c4ec..a71705fd085 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -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.  */
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C 
> b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C
> index 46273654f24..dcc404489be 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C
> @@ -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
> index 00000000000..93f3f10898e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-diag2.C
> @@ -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" }
> diff --git a/gcc/testsuite/g++.dg/cpp1y/pr63996.C 
> b/gcc/testsuite/g++.dg/cpp1y/pr63996.C
> index 8eee2e0af30..347c86cc63c 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/pr63996.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/pr63996.C
> @@ -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);
> diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit18.C 
> b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit18.C
> index 8e230ef3bc3..f8918148cda 100644
> --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit18.C
> +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit18.C
> @@ -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 }
> diff --git a/gcc/testsuite/g++.dg/template/explicit-args6.C 
> b/gcc/testsuite/g++.dg/template/explicit-args6.C
> index 18663d7bcf9..0d9718c38f6 100644
> --- a/gcc/testsuite/g++.dg/template/explicit-args6.C
> +++ b/gcc/testsuite/g++.dg/template/explicit-args6.C
> @@ -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()
>  {
> 
> base-commit: dbffeadf7f682625c7ac8c251ee62c02c90f32df
> -- 
> 2.49.0
> 
> 

Reply via email to