From patchwork Mon Sep 18 17:21:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 141489 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:172:b0:3f2:4152:657d with SMTP id h50csp2823722vqi; Mon, 18 Sep 2023 10:22:13 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGJwYC6mi1zlbfmrJRyFKOsEA3/sPPQbIvL9DIL8lL1N8XmtKo29JkYCq9N/YiyjZj6DCv8 X-Received: by 2002:a17:906:ca:b0:9a9:e4f8:3501 with SMTP id 10-20020a17090600ca00b009a9e4f83501mr7411837eji.43.1695057732980; Mon, 18 Sep 2023 10:22:12 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1695057732; cv=none; d=google.com; s=arc-20160816; b=Ukxx3EhaZYjDm8In3XYtgugzY2JC5J+Jt/ZI4bbIA+bjdPmze8WhHrWC4Ufy7gNAYS L4eXE+v5/yxRnKd0nvGvg57BdsGpkMtqxN0wMrylK1VSXfCGpoNa68+yZVIUpUNeH848 k1l0+K6r9CM9O6CKhWlQuMZUlJpzTwCQ5e7Y5+EhlkRXjcl7wG7uLx+z/L3NcgYb7J2J NiFG8UpCafNx0QXmft/uXU77Vgj5mluFKY6wOlYh43SwSj7vJ7o9ntjqnHxxaLZgLD/z W+RLx7djG0e8yto+jIIc3qyU6GQizeHy3w0+tCusmc0/rwxjILwdmL2PjFeXpGccblXK xQeA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:from:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence :content-transfer-encoding:content-disposition:in-reply-to :mime-version:references:message-id:subject:cc:to:date:dmarc-filter :delivered-to:dkim-signature:dkim-filter; bh=5aMoQLyhJ9gPVQDCPBbK0DgLPOUlWGZw9+WxwNpeRUk=; fh=0xZT+NBKSeH8qOu04/61f1ZGePpF4jF/gxp331YE14k=; b=v48vdwpjZTtdQJI3UDULnbaevRY9frkbv0BWSv3BUX4gSoyxfrsA+M3GLkmzkIPbbo E34Qv8Y2OQbEA984HQVf2uyCNXoYCqadY7gNkH7KIVfPH/chJx2FkEtHsDnTK0/YfEev xiqDQe9ok5EYO1ZRpHkRWP1n/+MvTYgDFAgVWMsP2ZJaE0+gdtmMK7C1BJGVmJthmlbP sqbUQl2byZL0dmQPPQewlxXqSSWWHjLAW0wz7EYPmMAYoOcwhstA4rh5y5/bUfgDmr/X AgMTOZNkbeOfGLgctCktFDUXZylEJdct/9cZ8KE9c8CZIbR3e14QoJB4HYo8NyT9xfJ2 bKIA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=ouey1eE4; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id uz27-20020a170907119b00b0099d0a0914b1si7906277ejb.203.2023.09.18.10.22.12 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 18 Sep 2023 10:22:12 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=ouey1eE4; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 63B4B3857736 for ; Mon, 18 Sep 2023 17:22:11 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 63B4B3857736 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1695057731; bh=5aMoQLyhJ9gPVQDCPBbK0DgLPOUlWGZw9+WxwNpeRUk=; h=Date:To:Cc:Subject:References:In-Reply-To:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=ouey1eE4iagQIqsBEDqszCFmulERFVX6irGRpge8UITARLiKbuySht78iRNl8i/m1 6cjDZPRol8yQ7zVJafDwxgOyFM/OC9ltOMmhcQyQWkroNf2OMhrQo7YiIZNtmJquwM aFr8to64t0Qu5FajM0MP+ODwxQLglTpKBMuL3fJs= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org 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 33C583858C2B for ; Mon, 18 Sep 2023 17:21:15 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 33C583858C2B Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-218-SO8b7_7JNeeD2LzmjjK-BQ-1; Mon, 18 Sep 2023 13:21:12 -0400 X-MC-Unique: SO8b7_7JNeeD2LzmjjK-BQ-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.rdu2.redhat.com [10.11.54.2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 5D7AA1C07580 for ; Mon, 18 Sep 2023 17:21:12 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.45.224.82]) by smtp.corp.redhat.com (Postfix) with ESMTPS id DC3D640C6EA8; Mon, 18 Sep 2023 17:21:11 +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 38IHLAwu2519834 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Mon, 18 Sep 2023 19:21:10 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 38IHL9R52519833; Mon, 18 Sep 2023 19:21:09 +0200 Date: Mon, 18 Sep 2023 19:21:09 +0200 To: Jason Merrill Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] c++, v2: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348] Message-ID: References: MIME-Version: 1.0 In-Reply-To: X-Scanned-By: MIMEDefang 3.1 on 10.11.54.2 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-3.7 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_H3, 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 X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Jakub Jelinek via Gcc-patches From: Jakub Jelinek Reply-To: Jakub Jelinek Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1777396857072114705 X-GMAIL-MSGID: 1777396857072114705 On Thu, Aug 24, 2023 at 04:30:51PM +0200, Jakub Jelinek via Gcc-patches wrote: > 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. 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) 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 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 Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2023-09-18 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. Jakub --- gcc/c-family/c-cppbuiltin.cc.jj 2023-09-18 12:42:16.294388148 +0200 +++ gcc/c-family/c-cppbuiltin.cc 2023-09-18 13:09:47.155441065 +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-09-18 13:08:31.116453667 +0200 +++ gcc/cp/parser.cc 2023-09-18 13:09:47.161440985 +0200 @@ -16601,6 +16601,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. */ @@ -16631,10 +16632,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) { @@ -16653,8 +16654,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-09-05 17:26:51.849921954 +0200 +++ gcc/cp/semantics.cc 2023-09-18 14:31:55.269431759 +0200 @@ -11379,6 +11379,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 @@ -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); + 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; + } + if (tree s + = cp_get_callee_fndecl_nofold (extract_call_expr (message_sz))) + if (!DECL_DECLARED_CONSTEXPR_P (s)) + warning_at (location, 0, "% message %qs " + "member not %", "size()"); + 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; + } + if (tree d + = cp_get_callee_fndecl_nofold (extract_call_expr (message_data))) + if (!DECL_DECLARED_CONSTEXPR_P (d)) + warning_at (location, 0, "% message %qs " + "member not %", "data()"); + 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. */ @@ -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, + "% message % member " + "function must be a constant expression"); + 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, + "% 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 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, + "% message % " + "member function must be a 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); @@ -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 } +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 } +#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 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 "'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 } + 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 } } + // { 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