From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id C2B4D3858C53 for ; Thu, 24 Aug 2023 14:30:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C2B4D3858C53 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1692887457; h=from:from:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=zLpjv6GDub0nkKXkABtpjjXayoL117xuuIy7Pjjrcqs=; b=E4KFllcpAO+Pc1CW6akxNPm1JQLiCwi1vL/JnzniNUErxQqnKCrmANJphTBVFMhMxlhauD YiennZmUtPOA0FWy5jTpDi3qYrtvx8UyasjxnjNd5tXELy8rwanIC7R9wMijxveu4fmn8Z HAvgfNPxYmSvCSraZV/GGfwuMD619gU= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-231-6xlu4U3KOcaCbgjmJ53WzQ-1; Thu, 24 Aug 2023 10:30:55 -0400 X-MC-Unique: 6xlu4U3KOcaCbgjmJ53WzQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id A80388D40AE for ; Thu, 24 Aug 2023 14:30:53 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.45.225.165]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 370DAC1602B; Thu, 24 Aug 2023 14:30:53 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.17.1/8.17.1) with ESMTPS id 37OEUpOq667890 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Thu, 24 Aug 2023 16:30:51 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 37OEUpNU667889; Thu, 24 Aug 2023 16:30:51 +0200 Date: Thu, 24 Aug 2023 16:30:51 +0200 From: Jakub Jelinek To: Jason Merrill Cc: gcc-patches@gcc.gnu.org, Jonathan Wakely Subject: [PATCH] c++: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348] Message-ID: Reply-To: Jakub Jelinek MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-3.4 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,KAM_SHORT,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Hi! The following patch on top of PR110349 patch (weak dependency, only for -Wc++26-extensions, I could split that part into an independent patch) and PR110342 patch (again weak dependency, this time mainly because it touches the same code in cp_parser_static_assert and nearby spot in udlit-error1.C testcase) implements the user generated static_assert messages next to string literals. As I wrote already in the PR, in addition to looking through the paper I looked at the clang++ testcase for this feature implemented there from paper's author and on godbolt played with various parts of the testcase coverage below, and there are 4 differences between what the patch implements and what clang++ implements. The first is that clang++ diagnoses if M.size () or M.data () methods are present, but aren't constexpr; while the paper introduction talks about that, the standard wording changes don't seem to require that, all they say is that those methods need to exist (assuming accessible and the like) and be implicitly convertible to std::size_t or const char *, but rest is only if the static assertion fails. If there is intent to change that wording, the question is how far to go, e.g. while M.size () could be constexpr, they could e.g. return some class object which wouldn't have constexpr conversion operator to size_t/const char * and tons of other reasons why the constant evaluation could fail. Without actually evaluating it I don't see how we could guarantee anything for non-failed static_assert. The second and most important is that clang++ has a couple of tests (and the testcase below as well) where M.data () is not a core constant expression but M.data ()[0] ... M.data ()[M.size () - 1] is integer constant expression. From my reading of http://eel.is/c++draft/dcl.pre#11.2.2 that means those should be rejected (examples of these are e.g. static_assert (false, T{}); in the testcase, where T{}.data () returns pointer returned from new expression, but T{}'s destructor then deletes it, making it point to no longer live object. Or static_assert (false, a); where a.data () returns &a.a but because a is constexpr automatic variable, that isn't valid core constant expression, while a.data ()[0] is. There are a couple of others. Now, it seems allowing that is quite useful in real-world, but the question is with what standard changes to achieve that. One possibility would be s/a core constant/an/; from implementation POV that would mean that if M.size () is 0, then M.data () doesn't have to be constexpr at all. Otherwise, implementation could try to evaluate silently M.data () as constant expression, if it would be one, it could just use c_getstr in the GCC case as the patch does + optionally the 2 M.data ()[0] and M.data ()[M.size () - 1] tests to verify boundary cases more carefully. And if it wouldn't be one, it would need to evaluate M.data ()[i] for i in [0, M.size () - 1] to get all the characters one by one. Another possibility would be to require that say ((void) (M.data ()), 0) is a constant expression, that doesn't help much with the optimized way to get at the message characters, but would require that data () is constexpr even for the 0 case etc. The third difference is that static_assert (false, "foo"_myd); in the testcase is normal failed static assertion and static_assert (true, "foo"_myd); would be accepted, while clang++ rejects it. IMHO "foo"_myd doesn't match the syntactic requirements of unevaluated-string as mentioned in http://eel.is/c++draft/dcl.pre#10 , and because a constexpr udlit operator can return something which is valid, it shouldn't be rejected just in case. Last is clang++ ICEs on non-static data members size/data. The patch implements what I see in the paper, because it is unclear what further changes will be voted in (and the changes can be done at that point). The patch uses tf_none in 6 spots so that just the static_assert specific errors are emitted and not others, but it would certainly be possible to use complain instead of tf_none there, get more errors in some cases, but perhaps help users figure out what exactly is wrong in detail. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2023-08-24 Jakub Jelinek PR c++/110348 gcc/c-family/ * c-cppbuiltin.cc (c_cpp_builtins): For C++26 predefine __cpp_static_assert to 202306L rather than 201411L. gcc/cp/ * parser.cc: Implement C++26 P2741R3 - user-generated static_assert messages. (cp_parser_static_assert): Parse message argument as conditional-expression if it is not a pure string literal or several of them concatenated followed by closing paren. * semantics.cc (finish_static_assert): Handle message which is not STRING_CST. * pt.cc (tsubst_expr) : Also tsubst_expr message and make sure that if it wasn't originally STRING_CST, it isn't after tsubst_expr either. gcc/testsuite/ * g++.dg/cpp26/static_assert1.C: New test. * g++.dg/cpp26/feat-cxx26.C (__cpp_static_assert): Expect 202306L rather than 201411L. * g++.dg/cpp0x/udlit-error1.C: Expect different diagnostics for static_assert with user-defined literal. --- gcc/c-family/c-cppbuiltin.cc.jj 2023-08-23 16:09:15.146715860 +0200 +++ gcc/c-family/c-cppbuiltin.cc 2023-08-24 11:31:20.756788257 +0200 @@ -1023,7 +1023,8 @@ c_cpp_builtins (cpp_reader *pfile) { /* Set feature test macros for C++17. */ cpp_define (pfile, "__cpp_unicode_characters=201411L"); - cpp_define (pfile, "__cpp_static_assert=201411L"); + if (cxx_dialect <= cxx23) + cpp_define (pfile, "__cpp_static_assert=201411L"); cpp_define (pfile, "__cpp_namespace_attributes=201411L"); cpp_define (pfile, "__cpp_enumerator_attributes=201411L"); cpp_define (pfile, "__cpp_nested_namespace_definitions=201411L"); @@ -1087,6 +1088,7 @@ c_cpp_builtins (cpp_reader *pfile) /* Set feature test macros for C++26. */ cpp_define (pfile, "__cpp_constexpr=202306L"); cpp_define (pfile, "__cpp_placeholder_variables=202306L"); + cpp_define (pfile, "__cpp_static_assert=202306L"); } if (flag_concepts) { --- gcc/cp/parser.cc.jj 2023-08-23 16:09:15.157715719 +0200 +++ gcc/cp/parser.cc 2023-08-23 17:10:47.573754415 +0200 @@ -16427,6 +16427,7 @@ cp_parser_linkage_specification (cp_pars static_assert-declaration: static_assert ( constant-expression , string-literal ) ; static_assert ( constant-expression ) ; (C++17) + static_assert ( constant-expression, conditional-expression ) ; (C++26) If MEMBER_P, this static_assert is a class member. */ @@ -16457,10 +16458,10 @@ cp_parser_static_assert (cp_parser *pars /* Parse the constant-expression. Allow a non-constant expression here in order to give better diagnostics in finish_static_assert. */ - condition = - cp_parser_constant_expression (parser, - /*allow_non_constant_p=*/true, - /*non_constant_p=*/nullptr); + condition + = cp_parser_constant_expression (parser, + /*allow_non_constant_p=*/true, + /*non_constant_p=*/nullptr); if (cp_lexer_peek_token (parser->lexer)->type == CPP_CLOSE_PAREN) { @@ -16479,8 +16480,32 @@ cp_parser_static_assert (cp_parser *pars /* Parse the separating `,'. */ cp_parser_require (parser, CPP_COMMA, RT_COMMA); - /* Parse the string-literal message. */ - if (cxx_dialect >= cxx26) + /* Parse the message expression. */ + bool string_lit = true; + for (unsigned int i = 1; ; ++i) + { + cp_token *tok = cp_lexer_peek_nth_token (parser->lexer, i); + if (cp_parser_is_pure_string_literal (tok)) + continue; + else if (tok->type == CPP_CLOSE_PAREN) + break; + string_lit = false; + break; + } + if (!string_lit) + { + location_t loc = cp_lexer_peek_token (parser->lexer)->location; + if (cxx_dialect < cxx26) + pedwarn (loc, OPT_Wc__26_extensions, + "% with non-string message only " + "available with %<-std=c++2c%> or %<-std=gnu++2c%>"); + + message = cp_parser_conditional_expression (parser); + if (TREE_CODE (message) == STRING_CST) + message = build1_loc (loc, PAREN_EXPR, TREE_TYPE (message), + message); + } + else if (cxx_dialect >= cxx26) message = cp_parser_unevaluated_string_literal (parser); else message = cp_parser_string_literal (parser, /*translate=*/false, --- gcc/cp/semantics.cc.jj 2023-08-08 15:55:06.272167733 +0200 +++ gcc/cp/semantics.cc 2023-08-24 10:40:22.803103810 +0200 @@ -11337,6 +11337,7 @@ finish_static_assert (tree condition, tr bool member_p, bool show_expr_p) { tsubst_flags_t complain = tf_warning_or_error; + tree message_sz = NULL_TREE, message_data = NULL_TREE; if (message == NULL_TREE || message == error_mark_node @@ -11346,11 +11347,67 @@ 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); + 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); + if (message_sz == error_mark_node + || message_data == error_mark_node) + { + error_at (location, "% message must be a string " + "literal or object with % and " + "% members"); + return; + } + message_sz = perform_implicit_conversion (size_type_node, message_sz, + tf_none); + if (message_sz == error_mark_node) + { + error_at (location, "% message % member " + "function must be implicitly convertible to " + "%"); + return; + } + message_data = perform_implicit_conversion (const_string_type_node, + message_data, tf_none); + if (message_data == error_mark_node) + { + error_at (location, "% message % member " + "function must be implicitly convertible to " + "%"); + 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. */ @@ -11388,9 +11445,76 @@ 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, + "% message % member " + "function must be a constant expression"); + return; + } + len = tree_to_uhwi (message_sz); + message_data + = fold_non_dependent_expr (message_data, complain, + /*manifestly_const_eval=*/true); + if (!reduced_constant_expression_p (message_data)) + { + error_at (location, + "% message % member " + "function must be a constant expression"); + return; + } + if (len) + { + msg = c_getstr (message_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, + "% message % " + "member function must be a constant " + "expression"); + 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 + { + 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); @@ -11404,8 +11528,9 @@ finish_static_assert (tree condition, tr 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-08-23 16:09:15.160715681 +0200 +++ gcc/cp/pt.cc 2023-08-23 16:35:00.670093754 +0200 @@ -19429,15 +19429,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-08-24 11:27:52.909566524 +0200 +++ gcc/testsuite/g++.dg/cpp26/static_assert1.C 2023-08-24 14:44:59.775429645 +0200 @@ -0,0 +1,238 @@ +// 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 } +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 } } +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 } } +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 } +#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 } + +template +struct array { + constexpr size_t size () const { return N; } + constexpr const T *data () const { return a; } + const T a[N]; +}; +static_assert (true, array { 'O', 'K' }); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } +static_assert (true, array { 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 { 't', 'e', 's', 't' }); // { 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 } + +void +foo () +{ + constexpr auto a = array { '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_assert' message 'data\\\(\\\)' member function must be a constant expression" "" { 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_assert' message 'data\\\(\\\)' member function must be a constant expression" "" { 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_assert' message 'data\\\(\\\)' member function must be a constant expression" "" { 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 } +#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 + struct A { + constexpr int size () const = delete; + constexpr const char *data () const { return "test"; } }; + 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 } +#if __cpp_concepts >= 201907L + template + struct B { + constexpr int size () const { return 4; } + constexpr const char *data () const requires false { return "test"; } }; + static_assert (true, B{}); // { 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 + 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 + 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 fe; + G 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 } +} --- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj 2023-08-23 16:09:15.174715504 +0200 +++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 2023-08-24 11:29:53.247957973 +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-08-24 15:46:18.149708095 +0200 +++ gcc/testsuite/g++.dg/cpp0x/udlit-error1.C 2023-08-24 15:46:28.719574134 +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 } } + // { 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