public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jason Merrill <jason@redhat.com>
To: Jakub Jelinek <jakub@redhat.com>
Cc: gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++, v2: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348]
Date: Fri, 17 Nov 2023 09:18:39 -0500	[thread overview]
Message-ID: <9fdf48fa-8b74-4520-a602-996f94a40dce@redhat.com> (raw)
In-Reply-To: <c1d60a4b-2fa6-45cd-b74e-1246c8f24c66@redhat.com>


You recently pinged this patch, but I haven't seen an update since this 
review?

On 10/26/23 21:21, Jason Merrill wrote:
> On 9/18/23 13:21, Jakub Jelinek wrote:
>> Here is an updated version of the patch.
>> Compared to the last version, based on the discussion in the PR, the 
>> patch
>> 1) warns (but only that) if size()/data() methods aren't declared
>>     constexpr/consteval (or implicitly constexpr)
> 
> The language requirements also seem to be satisfied by
> 
> constexpr const char msg[] = "foo";
> struct A { constexpr int operator () () { return sizeof(msg); } };
> struct B { constexpr const char * operator()() { return msg; } };
> struct C {
>    A size;
>    B data;
> };
> constexpr int i = C().size();
> constexpr const char *p = C().data();
> static_assert (false, C());
> 
> constexpr int size() { return sizeof(msg); }
> constexpr const char *data() { return msg; }
> struct D {
>    int (*size)() = ::size;
>    const char *(*data)() = ::data;
> };
> constexpr int di = D().size();
> constexpr const char *dp = D().data();
> static_assert (false, D());
> 
> so we shouldn't assume that size/data are methods.
> 
>> 2) as I don't see a function which would determine if some expression
>>     is core constant expression (for the data() case), the patch just 
>> as an
>>     optimization tries to fold_nondependent_expr msg.data() expression
>>     quietly, if it is a constant expression, passes it to c_getstr if 
>> len > 0
>>     and if successful, only tries to constant expression evaluate
>>     msg.data()[0] and msg.data()[len - 1], otherwise it will constant
>>     expression evaluate the characters one by one;
>>     for the len == 0 case, it will fold_nondependent_expr + check 
>> result is
>>     integer_zero_node for (msg.data(), 0) which I think should fail if
>>     msg.data() is not a core constant expression, but succeed if it is
>>     even if it is not constant expression
> 
> Sounds good.
> 
>> 3) already the earlier version of the patch was passing
>>     manifestly_const_eval=true argument, you said in the PR you've raised
>>     it in CWG, I've newly added testsuite coverage for that
> 
> CWG agreed with this direction.
> 
>> --- gcc/cp/semantics.cc.jj    2023-09-05 17:26:51.849921954 +0200
>> +++ gcc/cp/semantics.cc    2023-09-18 14:31:55.269431759 +0200
>> @@ -11388,11 +11389,77 @@ finish_static_assert (tree condition, tr
>>     if (check_for_bare_parameter_packs (condition))
>>       condition = error_mark_node;
>> +  if (check_for_bare_parameter_packs (message))
>> +    return;
>> +
>> +  if (TREE_CODE (message) != STRING_CST
>> +      && !type_dependent_expression_p (message))
>> +    {
>> +      message_sz
>> +    = finish_class_member_access_expr (message,
>> +                       get_identifier ("size"),
>> +                       false, tf_none);
>> +      if (TREE_CODE (message_sz) != COMPONENT_REF)
>> +    message_sz = error_mark_node;
>> +      if (message_sz != error_mark_node)
>> +    message_sz = build_new_method_call (message,
>> +                        TREE_OPERAND (message_sz, 1),
>> +                        NULL, NULL_TREE, LOOKUP_NORMAL,
>> +                        NULL, tf_none);
> 
> This should probably use finish_call_expr instead of 
> build_new_method_call because of my example above, and also so you don't 
> need to pull out the TREE_OPERAND or check the result of name lookup.
> 
>> +      message_data
>> +    = finish_class_member_access_expr (message,
>> +                       get_identifier ("data"),
>> +                       false, tf_none);
>> +      if (TREE_CODE (message_data) != COMPONENT_REF)
>> +    message_data = error_mark_node;
>> +      if (message_data != error_mark_node)
>> +    message_data = build_new_method_call (message,
>> +                          TREE_OPERAND (message_data, 1),
>> +                          NULL, NULL_TREE, LOOKUP_NORMAL,
>> +                          NULL, tf_none);
> 
> Likewise.
> 
>> +      if (message_sz == error_mark_node
>> +      || message_data == error_mark_node)
>> +    {
>> +      error_at (location, "%<static_assert%> message must be a string "
>> +                  "literal or object with %<size()%> and "
>> +                  "%<data()%> members");
> 
> This diagnostic should be just if the calls to 
> finish_class_member_access_expr fail; better to get the normal 
> diagnostics from finish_call_expr if the calls fail for whatever reason.
> 
>> +      return;
>> +    }
>> +      if (tree s
>> +      = cp_get_callee_fndecl_nofold (extract_call_expr (message_sz)))
>> +    if (!DECL_DECLARED_CONSTEXPR_P (s))
>> +      warning_at (location, 0, "%<static_assert%> message %qs "
>> +                   "member not %<constexpr%>", "size()");
>> +      message_sz = perform_implicit_conversion (size_type_node, 
>> message_sz,
>> +                        tf_none);
> 
> This should probably use build_converted_constant_expr?
> 
>> +      if (message_sz == error_mark_node)
>> +    {
>> +      error_at (location, "%<static_assert%> message %<size()%> member "
>> +                  "function must be implicitly convertible to "
>> +                  "%<std::size_t%>");
>> +      return;
>> +    }
>> +      if (tree d
>> +      = cp_get_callee_fndecl_nofold (extract_call_expr (message_data)))
>> +    if (!DECL_DECLARED_CONSTEXPR_P (d))
>> +      warning_at (location, 0, "%<static_assert%> message %qs "
>> +                   "member not %<constexpr%>", "data()");
>> +      message_data = perform_implicit_conversion 
>> (const_string_type_node,
>> +                          message_data, tf_none);
>> +      if (message_data == error_mark_node)
>> +    {
>> +      error_at (location, "%<static_assert%> message %<data()%> member "
>> +                  "function must be implicitly convertible to "
>> +                  "%<const char*%>");
>> +      return;
>> +    }
>> +    }
>>     /* Save the condition in case it was a concept check.  */
>>     tree orig_condition = condition;
>> -  if (instantiation_dependent_expression_p (condition))
>> +  if (instantiation_dependent_expression_p (condition)
>> +      || instantiation_dependent_expression_p (message))
>>       {
>>         /* We're in a template; build a STATIC_ASSERT and put it in
>>            the right place. */
>> @@ -11430,9 +11497,88 @@ finish_static_assert (tree condition, tr
>>         if (processing_template_decl)
>>           goto defer;
>> -      int sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT
>> -                     (TREE_TYPE (TREE_TYPE (message))));
>> -      int len = TREE_STRING_LENGTH (message) / sz - 1;
>> +      int len;
>> +      const char *msg = NULL;
>> +      char *buf = NULL;
>> +      if (message_sz && message_data)
>> +        {
>> +          message_sz
>> +        = fold_non_dependent_expr (message_sz, complain,
>> +                       /*manifestly_const_eval=*/true);
>> +          if (!tree_fits_uhwi_p (message_sz)
>> +          || ((unsigned HOST_WIDE_INT) (int) tree_to_uhwi (message_sz)
>> +              != tree_to_uhwi (message_sz)))
>> +        {
>> +          error_at (location,
>> +                "%<static_assert%> message %<size()%> member "
>> +                "function must be a constant expression");
> 
> This can use cxx_constant_value to show what makes it not a 
> constant-expression.  And also don't assume size is a member function.
> 
>> +          return;
>> +        }
>> +          len = tree_to_uhwi (message_sz);
>> +          tree data
>> +        = fold_non_dependent_expr (message_data, tf_none,
>> +                       /*manifestly_const_eval=*/true);
>> +          if (!reduced_constant_expression_p (data))
>> +        data = NULL_TREE;
>> +          if (len)
>> +        {
>> +          if (data)
>> +            msg = c_getstr (data);
>> +          if (msg == NULL)
>> +            buf = XNEWVEC (char, len);
>> +          for (int i = 0; i < len; ++i)
>> +            {
>> +              tree t = message_data;
>> +              if (i)
>> +            t = build2 (POINTER_PLUS_EXPR,
>> +                    TREE_TYPE (message_data), message_data,
>> +                    size_int (i));
>> +              t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t);
>> +              t = fold_non_dependent_expr (t, complain,
>> +                           /*manifestly_const_eval=*/
>> +                           true);
>> +              if (!tree_fits_shwi_p (t))
>> +            {
>> +              error_at (location,
>> +                    "%<static_assert%> message %<data()%> "
>> +                    "member function must be a constant "
>> +                    "expression");
> 
> Likewise.
> 
>> +              return;
>> +            }
>> +              if (msg == NULL)
>> +            buf[i] = tree_to_shwi (t);
>> +              /* If c_getstr worked, just verify the first and
>> +             last characters using constant evaluation.  */
>> +              else if (len > 2 && i == 0)
>> +            i = len - 2;
>> +            }
>> +          if (msg == NULL)
>> +            msg = buf; > +        }
>> +          else if (!data)
>> +        {
>> +          data = build2 (COMPOUND_EXPR, integer_type_node,
>> +                 message_data, integer_zero_node);
>> +          data = fold_non_dependent_expr (data, complain,
>> +                          /*manifestly_const_eval=*/
>> +                          true);
>> +          if (!integer_zerop (data))
>> +            {
>> +              error_at (location,
>> +                "%<static_assert%> message %<data()%> "
>> +                "member function must be a constant "
>> +                "expression");
> 
> Likewise.
> 
>> +              return;
>> +            }
>> +        }
>> +        }
>> +      else
>> +        {
>> +          tree eltype = TREE_TYPE (TREE_TYPE (message));
>> +          int sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (eltype));
>> +          msg = TREE_STRING_POINTER (message);
>> +          len = TREE_STRING_LENGTH (message) / sz - 1;
>> +        }
>>         /* See if we can find which clause was failing (for logical 
>> AND).  */
>>         tree bad = find_failing_clause (NULL, orig_condition);
>> @@ -11442,12 +11588,13 @@ finish_static_assert (tree condition, tr
>>         auto_diagnostic_group d;
>> -          /* Report the error. */
>> +      /* Report the error. */
>>         if (len == 0)
>>           error_at (cloc, "static assertion failed");
>>         else
>> -        error_at (cloc, "static assertion failed: %s",
>> -              TREE_STRING_POINTER (message));
>> +        error_at (cloc, "static assertion failed: %.*s", len, msg);
>> +
>> +      XDELETEVEC (buf);
>>         diagnose_failing_condition (bad, cloc, show_expr_p);
>>       }
>> --- gcc/cp/pt.cc.jj    2023-09-18 12:42:16.309387948 +0200
>> +++ gcc/cp/pt.cc    2023-09-18 13:09:47.166440918 +0200
>> @@ -19430,15 +19430,20 @@ tsubst_expr (tree t, tree args, tsubst_f
>>       case STATIC_ASSERT:
>>         {
>> -    tree condition;
>> +    tree condition, message;
>>       ++c_inhibit_evaluation_warnings;
>>       condition = tsubst_expr (STATIC_ASSERT_CONDITION (t), args,
>>                    complain, in_decl);
>> +    message = tsubst_expr (STATIC_ASSERT_MESSAGE (t), args,
>> +                   complain, in_decl);
>> +    if (TREE_CODE (STATIC_ASSERT_MESSAGE (t)) != STRING_CST
>> +        && TREE_CODE (message) == STRING_CST)
>> +      message = build1_loc (STATIC_ASSERT_SOURCE_LOCATION (t),
>> +                PAREN_EXPR, TREE_TYPE (message), message);
>>       --c_inhibit_evaluation_warnings;
>> -        finish_static_assert (condition,
>> -                              STATIC_ASSERT_MESSAGE (t),
>> +    finish_static_assert (condition, message,
>>                                 STATIC_ASSERT_SOURCE_LOCATION (t),
>>                     /*member_p=*/false, /*show_expr_p=*/true);
>>         }
>> --- gcc/testsuite/g++.dg/cpp26/static_assert1.C.jj    2023-09-18 
>> 13:09:47.167440904 +0200
>> +++ gcc/testsuite/g++.dg/cpp26/static_assert1.C    2023-09-18 
>> 15:04:25.402596093 +0200
>> @@ -0,0 +1,284 @@
>> +// C++26 P2741R3 - user-generated static_assert messages
>> +// { dg-do compile { target c++11 } }
>> +// { dg-options "" }
>> +
>> +static_assert (true, "");
>> +static_assert (true, (""));    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
>> +static_assert (true, "" + 0);    // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
>> +static_assert (true, 0);    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
>> +struct A {};
>> +static_assert (true, A {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
>> +struct B { int size; };
>> +static_assert (true, B {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
>> +struct C { constexpr int size () const { return 0; } };
>> +static_assert (true, C {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
>> +struct D { constexpr int size () const { return 0; } int data; };
>> +static_assert (true, D {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
>> +struct E { int size = 0;
>> +       constexpr const char *data () const { return ""; } };
>> +static_assert (true, E {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
> 
> The diagnostic states properties that E satisfies, and not the problem 
> (that E{}.size isn't invocable).
> 
>> +struct F { constexpr const char *size () const { return ""; }
>> +       constexpr const char *data () const { return ""; } };
>> +static_assert (true, F {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "'static_assert' message 'size\\\(\\\)' 
>> member function must be implicitly convertible to 'std::size_t'" "" { 
>> target *-*-* } .-1 }
>> +struct G { constexpr long size () const { return 0; }
>> +       constexpr float data () const { return 0.0f; } };
>> +static_assert (true, G {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "'static_assert' message 'data\\\(\\\)' 
>> member function must be implicitly convertible to 'const char\\\*'" "" 
>> { target *-*-* } .-1 }
>> +struct H { short size () const { return 0; }
>> +       constexpr const char *data () const { return ""; } };
>> +static_assert (true, H {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +                // { dg-warning "'static_assert' message 
>> 'size\\\(\\\)' member not 'constexpr'" "" { target *-*-* } .-1 }
>> +struct I { constexpr signed char size () const { return 0; }
>> +       const char *data () const { return ""; } };
>> +static_assert (true, I {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +                // { dg-warning "'static_assert' message 
>> 'data\\\(\\\)' member not 'constexpr'" "" { target *-*-* } .-1 }
>> +struct J { constexpr int size () const { return j ? throw 1 : 0; }
>> +       constexpr const char *data () const { return ""; };
>> +       constexpr J (int x) : j (x) {}
>> +       int j; };
>> +static_assert (true, J (1));    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +static_assert (false, J (0));    // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "static assertion failed" "" { target 
>> *-*-* } .-1 }
>> +static_assert (false, J (1));    // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "'static_assert' message 'size\\\(\\\)' 
>> member function must be a constant expression" "" { target *-*-* } .-1 }
>> +struct K { constexpr operator int () { return 4; } };
>> +struct L { constexpr operator const char * () { return "test"; } };
>> +struct M { constexpr K size () const { return {}; }
>> +       constexpr L data () const { return {}; } };
>> +static_assert (true, M {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +static_assert (false, M {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "static assertion failed: test" "" { 
>> target *-*-* } .-1 }
>> +#if  __cpp_constexpr_dynamic_alloc >= 201907L
>> +struct N { constexpr int size () const { return 3; }
>> +       constexpr const char *data () const { return new char[3] { 
>> 'b', 'a', 'd' }; } };
>> +static_assert (true, N {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target { c++20 && 
>> c++23_down } } }
>> +static_assert (false, N {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target { c++20 && 
>> c++23_down } } }
>> +                // { dg-error "'static_assert' message 'data\\\(\\\)' 
>> member function must be a constant expression" "" { target c++20 } .-1 }
> 
> The cxx_constant_value diagnostics should be helpful here.
> 
>> +#endif
>> +constexpr const char a[] = { 't', 'e', 's', 't' };
>> +struct O { constexpr int size () const { return 4; }
>> +       constexpr const char *data () const { return a; } };
>> +static_assert (false, O {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "static assertion failed: test" "" { 
>> target *-*-* } .-1 }
>> +struct P { constexpr int size () const { return 4 - p; }
>> +       constexpr const char *data () const { return &a[p]; }
>> +       constexpr P (int x) : p (x) {}
>> +       int p; };
>> +static_assert (false, P (0));    // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "static assertion failed: test" "" { 
>> target *-*-* } .-1 }
>> +static_assert (false, P (2));    // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "static assertion failed: st" "" { 
>> target *-*-* } .-1 }
>> +struct Q { constexpr int size () const { return 4 - q; }
>> +       constexpr const char *data () const { return &"test"[q]; }
>> +       constexpr Q (int x) : q (x) {}
>> +       int q; };
>> +static_assert (false, Q (0));    // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "static assertion failed: test" "" { 
>> target *-*-* } .-1 }
>> +static_assert (false, Q (1));    // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "static assertion failed: est" "" { 
>> target *-*-* } .-1 }
>> +struct R { constexpr int size () const { return 4 - r; }
>> +       constexpr const char *d () const { return "test"; }
>> +       constexpr const char *data () const { return d () + r; }
>> +       constexpr R (int x) : r (x) {}
>> +       int r; };
>> +static_assert (false, R (0));    // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "static assertion failed: test" "" { 
>> target *-*-* } .-1 }
>> +static_assert (false, R (2));    // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "static assertion failed: st" "" { 
>> target *-*-* } .-1 }
>> +struct S { constexpr float size (float) const { return 42.0f; }
>> +       constexpr int size (void * = nullptr) const { return 4; }
>> +       constexpr double data (double) const { return 42.0; }
>> +       constexpr const char *data (int = 0) const { return "test"; } };
>> +static_assert (true, S {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +static_assert (false, S {});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
>> +                // { dg-error "static assertion failed: test" "" { 
>> target *-*-* } .-1 }
>> +
>> +using size_t = decltype (sizeof (0));
>> +struct string_view {
>> +  size_t s;
>> +  const char *d;
>> +  constexpr string_view () : s (0), d (nullptr) {}
>> +  constexpr string_view (const char *p) : s (__builtin_strlen (p)), d 
>> (p) {}
>> +  constexpr string_view (size_t l, const char *p) : s (l), d (p) {}
>> +  constexpr size_t size () const noexcept { return s; }
>> +  constexpr const char *data () const noexcept { return d; }
>> +};
>> +static_assert (true, string_view{});                // { dg-warning 
>> "'static_assert' with non-string message only available with" "" { 
>> target c++23_down } }
>> +static_assert (false, string_view ("test"));            // { 
>> dg-warning "'static_assert' with non-string message only available 
>> with" "" { target c++23_down } }
>> +                                // { dg-error "static assertion 
>> failed: test" "" { target *-*-* } .-1 }
>> +static_assert (false, string_view ("א"));            // { dg-warning 
>> "'static_assert' with non-string message only available with" "" { 
>> target c++23_down } }
>> +                                // { dg-error "static assertion 
>> failed: א" "" { target *-*-* } .-1 }
>> +static_assert (false, string_view (0, nullptr));        // { 
>> dg-warning "'static_assert' with non-string message only available 
>> with" "" { target c++23_down } }
>> +                                // { dg-error "static assertion 
>> failed" "" { target *-*-* } .-1 }
>> +static_assert (false, string_view (4, "testwithextrachars"));    // { 
>> dg-warning "'static_assert' with non-string message only available 
>> with" "" { target c++23_down } }
>> +                                // { dg-error "static assertion 
>> failed: test" "" { target *-*-* } .-1 }
>> +static_assert (false, string_view (42, "test"));        // { 
>> dg-warning "'static_assert' with non-string message only available 
>> with" "" { target c++23_down } }
>> +                                // { dg-error "'static_assert' 
>> message 'data\\\(\\\)' member function must be a constant expression" 
>> "" { target *-*-* } .-1 }
> 
> The cxx_constant_value diagnostics should be helpful here.
> 
>> +template <typename T, size_t N>
>> +struct array {
>> +  constexpr size_t size () const { return N; }
>> +  constexpr const T *data () const { return a; }
>> +  const T a[N];
>> +};
>> +static_assert (true, array<char, 2> { 'O', 'K' });        // { 
>> dg-warning "'static_assert' with non-string message only available 
>> with" "" { target c++23_down } }
>> +static_assert (true, array<wchar_t, 2> { L'O', L'K' });        // { 
>> dg-warning "'static_assert' with non-string message only available 
>> with" "" { target c++23_down } }
>> +                                // { dg-error "'static_assert' 
>> message 'data\\\(\\\)' member function must be implicitly convertible 
>> to 'const char\\\*'" "" { target *-*-* } .-1 }
>> +static_assert (false, array<char, 4> { 't', 'e', 's', 't' });    // { 
>> dg-warning "'static_assert' with non-string message only available 
>> with" "" { target c++23_down } }
>> +                                // { dg-error "static assertion 
>> failed: test" "" { target *-*-* } .-1 }
>> +
>> +void
>> +foo ()
>> +{
>> +  constexpr auto a = array<char, 4> { 't', 'e', 's', 't' };
>> +  static_assert (false, a);                    // { dg-warning 
>> "'static_assert' with non-string message only available with" "" { 
>> target c++23_down } }
>> +}                                // { dg-error "static assertion 
>> failed: test" "" { target *-*-* } .-1 }
>> +
>> +#if  __cpp_constexpr_dynamic_alloc >= 201907L
>> +struct T {
>> +  const char *d = init ();
>> +  constexpr int size () const { return 4; }
>> +  constexpr const char *data () const { return d; }
>> +  constexpr const char *init () const { return new char[4] { 't', 
>> 'e', 's', 't' }; }
>> +  constexpr ~T () { delete[] d; }
>> +};
>> +static_assert (false, T{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target { c++20 && 
>> c++23_down } } }
>> +                    // { dg-error "static assertion failed: test" "" 
>> { target c++20 } .-1 }
>> +#endif
>> +struct U { constexpr operator const char * () const { return u; }
>> +       char u[5] = "test"; };
>> +#if __cplusplus >= 201402L
>> +struct V { constexpr auto size () const { return K{}; }
>> +       constexpr auto data () const { return U{}; } };
>> +static_assert (false, V{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target { c++14 && 
>> c++23_down } } }
>> +                    // { dg-error "static assertion failed: test" "" 
>> { target c++14 } .-1 }
>> +#endif
>> +struct W { constexpr int size (int) const { return 4; }
>> +       constexpr const char *data () const { return "test"; } };
>> +static_assert (true, W{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                    // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
>> +struct X { constexpr int size () const { return 4; }
>> +       constexpr const char *data (int) const { return "test"; } };
>> +static_assert (true, X{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                    // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
>> +struct Y { constexpr int size () { return 4; }
>> +       constexpr const char *data (int) { return "test"; } };
>> +static_assert (true, Y{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                    // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
> 
> More cases of the diagnostic not stating the problem; as above, this 
> message should only be for when lookup fails.
> 
>> +#if __cpp_concepts >= 201907L
>> +struct Z { constexpr int size (auto...) const { return 4; }
>> +       constexpr const char *data (auto...) const { return "test"; } };
>> +static_assert (false, Z{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target { c++20 && 
>> c++23_down } } }
>> +                    // { dg-error "static assertion failed: test" "" 
>> { target c++20 } .-1 }
>> +#endif
>> +
>> +namespace NN
>> +{
>> +  template <typename T>
>> +  struct A {
>> +    constexpr int size () const = delete;
>> +    constexpr const char *data () const { return "test"; } };
>> +  static_assert (true, A<int>{});    // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                    // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
>> +#if __cpp_concepts >= 201907L
>> +  template <typename T>
>> +  struct B {
>> +    constexpr int size () const { return 4; }
>> +    constexpr const char *data () const requires false { return 
>> "test"; } };
>> +  static_assert (true, B<short>{});    // { dg-warning 
>> "'static_assert' with non-string message only available with" "" { 
>> target { c++20 && c++23_down } } }
>> +                    // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target c++20 } .-1 }
>> +#endif
>> +  class C {
>> +    constexpr int size () const = delete;
>> +    constexpr const char *data () const { return "test"; } };
>> +  static_assert (true, C{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                    // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
>> +#if __cplusplus >= 201402L
>> +  struct D {
>> +    constexpr int size () { return 4; }
>> +    constexpr int size () const { return 3; }
>> +    constexpr const char *data () { return "test"; }
>> +    constexpr const char *data () const { return "ehlo"; } };
>> +  static_assert (true, D{});    // { dg-warning "'static_assert' with 
>> non-string message only available with" "" { target { c++14 && 
>> c++23_down } } }
>> +  static_assert (false, D{});    // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target { c++14 && 
>> c++23_down } } }
>> +                // { dg-error "static assertion failed: test" "" { 
>> target c++14 } .-1 }
>> +#endif
>> +  struct E {
>> +    constexpr int size () const { return 4; }
>> +    constexpr const char *data () const { return "test"; } };
>> +  template <typename T>
>> +  struct F {
>> +    static_assert (false, T{});    // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +  };                // { dg-error "static assertion failed: test" "" 
>> { target *-*-* } .-1 }
>> +  template <typename T>
>> +  struct G {
>> +    static_assert (false, T{});    // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +  };                // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
>> +  F<E> fe;
>> +  G<long> gl;
>> +  constexpr E operator ""_myd (const char *, size_t) { return E{}; }
>> +  static_assert (false, "foo"_myd);    // { dg-warning 
>> "'static_assert' with non-string message only available with" "" { 
>> target c++23_down } }
>> +                    // { dg-error "static assertion failed: test" "" 
>> { target *-*-* } .-1 }
>> +  constexpr E operator + (const char *, const E &) { return E{}; }
>> +  static_assert (false, "foo" + E{});    // { dg-warning 
>> "'static_assert' with non-string message only available with" "" { 
>> target c++23_down } }
>> +                    // { dg-error "static assertion failed: test" "" 
>> { target *-*-* } .-1 }
>> +  struct H {
>> +    static constexpr int size () { return 7; }
>> +    static constexpr const char *data () { return "message"; } };
>> +  static_assert (true, H{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +  static_assert (false, H{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                    // { dg-error "static assertion failed: message" 
>> "" { target *-*-* } .-1 }
>> +  struct I {
>> +    static constexpr int size () { return 0; }
>> +    static constexpr const char *data () { return nullptr; } };
>> +  static_assert (true, I{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +  static_assert (false, I{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_down } }
>> +                    // { dg-error "static assertion failed" "" { 
>> target *-*-* } .-1 }
>> +#if __cplusplus >= 201402L
>> +  struct J {
>> +    static constexpr int size () { return 0; }
>> +    static constexpr const char *data (int x = 0) { if (x) return 
>> nullptr; else throw 1; } };
>> +  static_assert (true, J{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target { c++14 && 
>> c++23_down } } }
>> +  static_assert (false, J{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target { c++14 && 
>> c++23_down } } }
>> +                    // { dg-error "'static_assert' message 
>> 'data\\\(\\\)' member function must be a constant expression" "" { 
>> target c++14 } .-1 }
>> +#endif
>> +#if __cpp_if_consteval >= 202106L
>> +  struct K {
>> +    static constexpr int size () { if consteval { return 4; } else { 
>> throw 1; } }
>> +    static constexpr const char *data () { return "test"; }
>> +  };
>> +  static_assert (true, K{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_only } }
>> +  static_assert (false, K{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_only } }
>> +                    // { dg-error "static assertion failed: test" "" 
>> { target c++23 } .-1 }
>> +  struct L {
>> +    static constexpr int size () { return 4; }
>> +    static constexpr const char *data () { if consteval { return 
>> "test"; } else { throw 1; } }
>> +  };
>> +  static_assert (true, L{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_only } }
>> +  static_assert (false, L{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_only } }
>> +                    // { dg-error "static assertion failed: test" "" 
>> { target c++23 } .-1 }
>> +  struct M {
>> +    static constexpr int size () { if consteval { throw 1; } else { 
>> return 4; } }
>> +    static constexpr const char *data () { return "test"; }
>> +  };
>> +  static_assert (true, M{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_only } }
>> +  static_assert (false, M{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_only } }
>> +                    // { dg-error "'static_assert' message 
>> 'size\\\(\\\)' member function must be a constant expression" "" { 
>> target c++23 } .-1 }
>> +  struct N {
>> +    static constexpr int size () { return 4; }
>> +    static constexpr const char *data () { if consteval { throw 1; } 
>> else { return "test"; } }
>> +  };
>> +  static_assert (true, N{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_only } }
>> +  static_assert (false, N{});        // { dg-warning "'static_assert' 
>> with non-string message only available with" "" { target c++23_only } }
>> +                    // { dg-error "'static_assert' message 
>> 'data\\\(\\\)' member function must be a constant expression" "" { 
>> target c++23 } .-1 }
>> +#endif
>> +}
>> --- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj    2023-09-18 
>> 12:42:16.327387707 +0200
>> +++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C    2023-09-18 
>> 13:09:47.167440904 +0200
>> @@ -304,8 +304,8 @@
>>   #ifndef __cpp_static_assert
>>   #  error "__cpp_static_assert"
>> -#elif __cpp_static_assert != 201411
>> -#  error "__cpp_static_assert != 201411"
>> +#elif __cpp_static_assert != 202306
>> +#  error "__cpp_static_assert != 202306"
>>   #endif
>>   #ifndef __cpp_namespace_attributes
>> --- gcc/testsuite/g++.dg/cpp0x/udlit-error1.C.jj    2023-09-18 
>> 13:08:31.530448184 +0200
>> +++ gcc/testsuite/g++.dg/cpp0x/udlit-error1.C    2023-09-18 
>> 13:09:47.167440904 +0200
>> @@ -11,7 +11,8 @@ void operator""_x(const char *, decltype
>>   #pragma message "hi"_x      // { dg-warning "string literal with 
>> user-defined suffix is invalid in this context" }
>>   extern "C"_x { void g(); } // { dg-error "before user-defined string 
>> literal" }
>> -static_assert(true, "foo"_x); // { dg-error "string literal with 
>> user-defined suffix is invalid in this context|expected" }
>> +static_assert(true, "foo"_x);    // { dg-error "'static_assert' with 
>> non-string message only available with" "" { target c++23_down } }
> 
> This diagnostic message seems unclear for a UDL?
> 
>> +                // { dg-error "'static_assert' message must be a 
>> string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' 
>> members" "" { target *-*-* } .-1 }
>>   [[deprecated("oof"_x)]]    // { dg-error "string literal with 
>> user-defined suffix is invalid in this context" "" { target c++26 } }
>>   void
>>
>>     Jakub
>>
> 


  reply	other threads:[~2023-11-17 14:18 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-08-24 14:30 [PATCH] c++: " Jakub Jelinek
2023-09-18 17:21 ` [PATCH] c++, v2: " Jakub Jelinek
2023-10-27  1:21   ` Jason Merrill
2023-11-17 14:18     ` Jason Merrill [this message]
2023-11-17 14:22       ` Jakub Jelinek
2023-11-21 17:17     ` [PATCH] c++, v3: " Jakub Jelinek
2023-11-21 17:23       ` Jakub Jelinek
2023-11-21 21:44       ` Jason Merrill
2023-11-21 22:19         ` Jakub Jelinek
2023-11-21 22:51           ` Jakub Jelinek
2023-11-22  3:51             ` Jason Merrill
2023-11-22 10:00               ` [PATCH] c++, v4: " Jakub Jelinek
2023-11-22 21:53                 ` Jason Merrill
2023-11-23  8:32                   ` Jakub Jelinek
2023-11-28 16:31                     ` Jason Merrill
2023-11-28 17:52                       ` Jakub Jelinek
2023-11-28 21:33                         ` Jason Merrill
2023-11-28 17:08                     ` Fix 'g++.dg/cpp26/static_assert1.C' for '-fno-exceptions' configurations (was: [PATCH] c++, v4: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348]) Thomas Schwinge
2023-11-28 17:11                       ` Fix 'g++.dg/cpp26/static_assert1.C' for '-fno-exceptions' configurations Jason Merrill
2023-11-29 13:19                         ` Thomas Schwinge

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=9fdf48fa-8b74-4520-a602-996f94a40dce@redhat.com \
    --to=jason@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jakub@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).