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.133.124]) by sourceware.org (Postfix) with ESMTPS id 16FA23858D33 for ; Thu, 23 Nov 2023 08:32:30 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 16FA23858D33 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 16FA23858D33 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700728355; cv=none; b=NoEtPqSw9S8Y8ody+9HOAMommGC3O7orOFLEJ1Vc0LnRztBXjrRyRN67sSX5am338awIeHnux4pQtQqSWfkIwssg4x0nk9LpAsi2afuhZyJMlPxotbK/68ayhROzJCml8xM7macF58OhjLybzL2GVkJWwTAmIBhI1G09q5LJyMw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700728355; c=relaxed/simple; bh=T3hp9QPK0ecCr5JxkPTHDL2GW9+xZQ/fQUvlL6IJ2fY=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=ABsJsIS9apAPkcV1oZXpbRTpx2h6hPLtcazjTQnCVDXJFqUauS9s4ZlhrNCHGzozvArPfX4SoOhWHfxdQNBTm4Y66VMHnbG7svY/QjhBirn7ZhqsblpQmpBs1qsS8K5LQOh+bkPlElXjSYmAAyPu7HHklW6WFeL5u95Xw5B6keU= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700728349; 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: in-reply-to:in-reply-to:references:references; bh=h0FztV9LDxy1sGqTpCOU7lrFE2AsTRdC4UYj7ITQMe8=; b=M/RhSYcATjEd4S0XJmMGss7Y32/YOeBzYVTGYRlxXBuXfnmrYA5XFbt9g3PiuIXR4O8RHC VyDyb0h0KM8g9n2yhwzh1neK4pULS7Y74IwPB02GVWQUgkX0KksKPPeqOMVf2M0a7od1FE nxsrQXQRjE4Ym53TVNZv8JfgOova/Pg= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-272-I4fxwt9DO-SxLA8Y0l8XEQ-1; Thu, 23 Nov 2023 03:32:28 -0500 X-MC-Unique: I4fxwt9DO-SxLA8Y0l8XEQ-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 211F1811E7B for ; Thu, 23 Nov 2023 08:32:28 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.39.194.53]) by smtp.corp.redhat.com (Postfix) with ESMTPS id A270A492BFA; Thu, 23 Nov 2023 08:32:27 +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 3AN8WP9G886312 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Thu, 23 Nov 2023 09:32:25 +0100 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 3AN8WP39886311; Thu, 23 Nov 2023 09:32:25 +0100 Date: Thu, 23 Nov 2023 09:32:24 +0100 From: Jakub Jelinek To: Jason Merrill Cc: gcc-patches@gcc.gnu.org Subject: Re: [PATCH] c++, v4: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348] Message-ID: Reply-To: Jakub Jelinek References: <4b133eec-f806-43af-800a-8339d50645fd@redhat.com> <79a9ad09-2feb-4f3b-a4fe-1d13293fcd8b@redhat.com> MIME-Version: 1.0 In-Reply-To: X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.10 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,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE 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: On Wed, Nov 22, 2023 at 04:53:48PM -0500, Jason Merrill wrote: > I agree it's weird to get two of the same error, but maybe instead of > duplicating the error, we could look up data only if size succeeded, and > then error once if either failed? Here is what I've committed after another bootstrap/regtest on x86_64-linux and i686-linux. Besides the above requested change I've tweaked 2 lines in the test not to rely on a particular std::size_t exact type because otherwise the test failed on i686-linux. And accepting there only the current unsigned int long unsigned int long long unsinged int unsigned __int20__ (or how exactly is this one spelled in diagnostics) seems fragile. Thanks a lot for the review of this (and sorry it took so long on my side because I've missed the first review). 2023-11-23 Jakub Jelinek PR c++/110348 gcc/ * doc/invoke.texi (-Wno-c++26-extensions): Document. gcc/c-family/ * c.opt (Wc++26-extensions): New option. * 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. For condition with bare parameter packs return early. * 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/doc/invoke.texi.jj 2023-11-22 10:14:56.021376360 +0100 +++ gcc/doc/invoke.texi 2023-11-22 10:17:41.328065157 +0100 @@ -9107,6 +9107,13 @@ Do not warn about C++23 constructs in co an older C++ standard. Even without this option, some C++23 constructs will only be diagnosed if @option{-Wpedantic} is used. +@opindex Wc++26-extensions +@opindex Wno-c++26-extensions +@item -Wno-c++26-extensions @r{(C++ and Objective-C++ only)} +Do not warn about C++26 constructs in code being compiled using +an older C++ standard. Even without this option, some C++26 constructs +will only be diagnosed if @option{-Wpedantic} is used. + @opindex Wcast-qual @opindex Wno-cast-qual @item -Wcast-qual --- gcc/c-family/c.opt.jj 2023-11-22 10:14:55.963377171 +0100 +++ gcc/c-family/c.opt 2023-11-22 10:17:41.328065157 +0100 @@ -498,6 +498,10 @@ Wc++23-extensions C++ ObjC++ Var(warn_cxx23_extensions) Warning Init(1) Warn about C++23 constructs in code compiled with an older standard. +Wc++26-extensions +C++ ObjC++ Var(warn_cxx26_extensions) Warning Init(1) +Warn about C++26 constructs in code compiled with an older standard. + Wcast-function-type C ObjC C++ ObjC++ Var(warn_cast_function_type) Warning EnabledBy(Wextra) Warn about casts between incompatible function types. --- gcc/c-family/c-cppbuiltin.cc.jj 2023-11-22 10:14:55.962377185 +0100 +++ gcc/c-family/c-cppbuiltin.cc 2023-11-22 10:17:41.329065143 +0100 @@ -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"); @@ -1086,6 +1087,7 @@ c_cpp_builtins (cpp_reader *pfile) { /* Set feature test macros for C++26. */ cpp_define (pfile, "__cpp_constexpr=202306L"); + cpp_define (pfile, "__cpp_static_assert=202306L"); } if (flag_concepts) { --- gcc/cp/parser.cc.jj 2023-11-22 10:14:55.969377087 +0100 +++ gcc/cp/parser.cc 2023-11-22 10:17:41.335065058 +0100 @@ -16616,6 +16616,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. */ @@ -16646,10 +16647,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) { @@ -16668,8 +16669,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-11-22 11:30:08.019325101 +0100 +++ gcc/cp/semantics.cc 2023-11-22 22:58:25.194480633 +0100 @@ -11434,6 +11434,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 @@ -11441,13 +11442,61 @@ finish_static_assert (tree condition, tr || condition == error_mark_node) return; - if (check_for_bare_parameter_packs (condition)) - condition = error_mark_node; + if (check_for_bare_parameter_packs (condition) + || 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, complain); + if (message_sz != error_mark_node) + message_data + = finish_class_member_access_expr (message, + get_identifier ("data"), + false, complain); + 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; + } + releasing_vec size_args, data_args; + message_sz = finish_call_expr (message_sz, &size_args, false, false, + complain); + message_data = finish_call_expr (message_data, &data_args, false, false, + complain); + if (message_sz == error_mark_node || message_data == error_mark_node) + return; + message_sz = build_converted_constant_expr (size_type_node, message_sz, + complain); + if (message_sz == error_mark_node) + { + error_at (location, "% message % " + "must be implicitly convertible to " + "%"); + return; + } + message_data = build_converted_constant_expr (const_string_type_node, + message_data, complain); + if (message_data == error_mark_node) + { + error_at (location, "% message % " + "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. */ @@ -11485,9 +11534,89 @@ 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) + { + tree msz = cxx_constant_value (message_sz, NULL_TREE, complain); + if (!tree_fits_uhwi_p (msz)) + { + error_at (location, + "% message % " + "must be a constant expression"); + return; + } + else if ((unsigned HOST_WIDE_INT) (int) tree_to_uhwi (msz) + != tree_to_uhwi (msz)) + { + error_at (location, + "% message % " + "%qE too large", msz); + return; + } + len = tree_to_uhwi (msz); + tree data = maybe_constant_value (message_data, NULL_TREE, + mce_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); + tree t2 = cxx_constant_value (t, NULL_TREE, complain); + if (!tree_fits_shwi_p (t2)) + { + error_at (location, + "% message % " + "must be a constant expression", i); + return; + } + if (msg == NULL) + buf[i] = tree_to_shwi (t2); + /* 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) + { + /* We don't have any function to test whether some + expression is a core constant expression. So, instead + test whether (message.data (), 0) is a constant + expression. */ + data = build2 (COMPOUND_EXPR, integer_type_node, + message_data, integer_zero_node); + tree t = cxx_constant_value (data, NULL_TREE, complain); + if (!integer_zerop (t)) + { + error_at (location, + "% message % " + "must be a core constant expression"); + 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); @@ -11497,12 +11626,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-11-22 10:14:55.992376766 +0100 +++ gcc/cp/pt.cc 2023-11-22 10:17:41.340064988 +0100 @@ -18701,15 +18701,20 @@ tsubst_stmt (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-11-22 10:17:41.340064988 +0100 +++ gcc/testsuite/g++.dg/cpp26/static_assert1.C 2023-11-22 10:47:45.045848504 +0100 @@ -0,0 +1,309 @@ +// 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 } + // { dg-error "request for member 'size' in '\\\(\\\"\\\"\\\)', which is of non-class type 'const char \\\[1\\\]'" "" { target *-*-* } .-2 } +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 } + // { dg-error "request for member 'size' in '\\\(const char\\\*\\\)\\\"\\\"', which is of non-class type 'const char\\\*'" "" { target *-*-* } .-2 } +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 } + // { dg-error "request for member 'size' in '0', which is of non-class type 'int'" "" { target *-*-* } .-2 } +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 } + // { dg-error "'struct A' has no member named 'size'" "" { target *-*-* } .-2 } +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 } + // { dg-error "'struct B' has no member named 'data'" "" { target *-*-* } .-2 } +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 } + // { dg-error "'struct C' has no member named 'data'" "" { target *-*-* } .-2 } +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 "'D\\\(\\\).D::data' cannot be used as a function" "" { 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 "'E\\\(\\\).E::size' cannot be used as a function" "" { target c++11_only } .-1 } + // { dg-error "'E\\\{0\\\}.E::size' cannot be used as a function" "" { target c++14 } .-2 } +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\\\(\\\)' must be implicitly convertible to 'std::size_t'" "" { target *-*-* } .-1 } + // { dg-error "could not convert 'F\\\(\\\).F::size\\\(\\\)' from 'const char\\\*' to '\[^']*'" "" { target *-*-* } .-2 } + // { dg-error "conversion from 'const char\\\*' to '\[^']*' in a converted constant expression" "" { target *-*-* } .-3 } +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\\\(\\\)' must be implicitly convertible to 'const char\\\*'" "" { target *-*-* } .-1 } + // { dg-error "could not convert 'G\\\(\\\).G::data\\\(\\\)' from 'float' to 'const char\\\*'" "" { target *-*-* } .-2 } +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; } // { dg-error "expression '' is not a constant expression" } + 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\\\(\\\)' 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' }; } }; // { dg-error "'\\\* N\\\(\\\).N::data\\\(\\\)' is not a constant expression because allocated storage has not been deallocated" "" { target c++20 } } +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\\\(\\\)\\\[0\\\]' 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 "array subscript value '41' is outside the bounds of array type 'const char \\\[5\\\]'" "" { target *-*-* } .-1 } + // { dg-error "'static_assert' message 'data\\\(\\\)\\\[41\\\]' must be a constant expression" "" { target *-*-* } .-2 } + +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\\\(\\\)' must be implicitly convertible to 'const char\\\*'" "" { target *-*-* } .-1 } + // { dg-error "could not convert 'array{const wchar_t \\\[2\\\]{\[0-9]+, \[0-9]+}}.array::data\\\(\\\)' from 'const wchar_t\\\*' to 'const char\\\*'" "" { target *-*-* } .-2 } +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 assertion failed: test" "" { 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 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 "no matching function for call to 'W::size\\\(\\\)'" "" { 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 "no matching function for call to 'X::data\\\(\\\)'" "" { 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 "no matching function for call to 'Y::data\\\(\\\)'" "" { 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 "use of deleted function 'constexpr int NN::A::size\\\(\\\) const \\\[with T = int\\\]'" "" { 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 "no matching function for call to 'NN::B::data\\\(\\\)'" "" { 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 "use of deleted function 'constexpr int NN::C::size\\\(\\\) const'" "" { target *-*-* } .-1 } + // { dg-error "'constexpr const char\\\* NN::C::data\\\(\\\) const' is private within this context" "" { target *-*-* } .-2 } +#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 } + // { dg-error "request for member 'size' in '0', which is of non-class type 'long int'" "" { target *-*-* } .-2 } + 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 } + 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; } }; // { dg-error "expression '' is not a constant expression" "" { target c++14 } } + 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\\\(\\\)' must be a core 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; } } // { dg-error "expression '' is not a constant expression" "" { target c++23 } } + 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\\\(\\\)' 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"; } } // { dg-error "expression '' is not a constant expression" "" { target c++23 } } + }; + 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\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++23 } .-1 } +#endif + struct O { constexpr int operator () () const { return 12; } }; + struct P { constexpr const char *operator () () const { return "another test"; } }; + struct Q { O size; P data; }; + static_assert (true, Q ()); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } + static_assert (false, Q {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } + // { dg-error "static assertion failed: another test" "" { target *-*-* } .-1 } + constexpr int get_size () { return 16; } + constexpr const char *get_data () { return "yet another test"; } + struct R { int (*size) () = NN::get_size; + const char *(*data) () = NN::get_data; }; + static_assert (true, R ()); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } + static_assert (false, R {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } } + // { dg-error "static assertion failed: yet another test" "" { target *-*-* } .-1 } +} --- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj 2023-11-22 10:14:56.057375857 +0100 +++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 2023-11-22 10:17:41.340064988 +0100 @@ -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-11-22 10:14:56.037376137 +0100 +++ gcc/testsuite/g++.dg/cpp0x/udlit-error1.C 2023-11-22 10:38:53.298282525 +0100 @@ -11,7 +11,9 @@ 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 } + // { dg-error "invalid use of 'void'" "" { target *-*-* } .-2 } [[deprecated("oof"_x)]] // { dg-error "string literal with user-defined suffix is invalid in this context" "" { target c++26 } } void Jakub