From patchwork Tue Nov 21 17:17:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 167849 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:2b07:b0:403:3b70:6f57 with SMTP id io7csp780845vqb; Tue, 21 Nov 2023 09:17:45 -0800 (PST) X-Google-Smtp-Source: AGHT+IFWmFJqTfGglvbUfnRfYIjhesb0yOoWweMVuNR02Z3H72+/ypDHoPc8Mq/HEW3xbyubPEL0 X-Received: by 2002:a05:6214:f04:b0:672:1d32:9d37 with SMTP id gw4-20020a0562140f0400b006721d329d37mr12990724qvb.26.1700587064952; Tue, 21 Nov 2023 09:17:44 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1700587064; cv=pass; d=google.com; s=arc-20160816; b=UceNFU0bAN3iwEQ5ta+sXsmMbHckKK0pQ0c4iFHAWqfSdtSnOhxQIfTIEh3GBEwHlY NNNSTOwI9IUsejCDckXu4K+QsBlP80ErgzGInKp3RzcurOdZYsBUz3qEH9vhuSgiihnv LRqYQ5LFHTGmgpNs+9HpP26QCLCqyDmrXumVZrsqFIic3QICiMVDA4r3f928HuWlGJVX +/ZW63GfrWQE1APH8rK3mo2N7DEvG/M4cqzc5YzW6owVUGUIoR0f0pKi1Oao2sMM+qXI NiTDf6XMniJo2xXFOZwIrW6zf+4rWWcvlTbwAg+jVILxY0mIS5ca0ggmxDKINJbjhVYW Cp+Q== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=errors-to:reply-to: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:from:date:dkim-signature:arc-filter:dmarc-filter :delivered-to; bh=RjZf42po0TTx21/l3sQCRckqtFfuqZ57oFoBpSgNMzs=; fh=0xZT+NBKSeH8qOu04/61f1ZGePpF4jF/gxp331YE14k=; b=fpTjE0NcROLdYPNn4lk6K5+/+Ntrrz72oD2/8ub5BitxVhHdleBXROl8LWYXqj2ODU JU2w8FSVTr9j5497+mXKX90SOai+T6Lm+33VOk/Jj1MT8jw0sUoDqcvLsndl2nMBkbWg qB/SugaYVLHinxVOeKRSW6PnhXFvJqqJ+8FkNdwJFgPr406P3Jqq9wzWeUuIEANC84Qd N0LOc2EJ21TAX5OOhgLulHT+IXyDMq9Y55lOe6WqjFBIJ+oPc/y9oXMvhxRM5beem+Kx wlXQ8VHCWOVu8+1N+MGnp+zOosuTeOlGysGWKm03bsgPxDkJPVii9QqyzznPxqCaQcx+ 425A== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=EJ10E0s2; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (server2.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id dv12-20020ad44eec000000b00679ca3e1384si6663575qvb.436.2023.11.21.09.17.44 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Nov 2023 09:17:44 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=EJ10E0s2; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id AE3943858435 for ; Tue, 21 Nov 2023 17:17:44 +0000 (GMT) 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 C9D773858D33 for ; Tue, 21 Nov 2023 17:17:09 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C9D773858D33 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 C9D773858D33 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=1700587034; cv=none; b=fSJAhffC+00fbQvtxepSQZMOkzMfLg9cgDI6Sfxkag8fxSUFJl6SbalLigi/sLJ97aG6B2INGNUEr78lRMiSKq9+SmZy9r4rHPut1eg136XQNYfsYTS36qXnmk6UtdnI8AepyaaOBnft5RF7YUSUyttx6liq+pj6FsmxVNdEsXU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700587034; c=relaxed/simple; bh=0URozrI/CPk3LfY4lz4mAGYy3SRO+Yb8YFEUZC0UPQ0=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=IR0PeNTD/1CzQ50j85x5LqkwdQnB8XEDYsuM6FR8FZP+Xs9nhylxp0rlHl5/iQNy6StlZGjINkWrTLbA56sr8u+qk5KFS7HQxv7jvZ2P7M80iB0V+I5hGswXMpkxvPLEHrIKGmVaEvs36jzNSjbDWfNBAQxrslbrYtrlKg5krYI= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700587029; 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=RjZf42po0TTx21/l3sQCRckqtFfuqZ57oFoBpSgNMzs=; b=EJ10E0s2XWTZpTBDi32zbQBEBUDwMfBG14EzbtMVxr6alcQ2OdNFyk2KpkSw0MAnwAPPBU k7wc3I4A1XMHn29TWxOYzCdomDIg38ra4bxDvK3Zs3tWx91iADdR+rdsVZF/KhNdk4gM82 6SbvYvMDj8d5idn/otz//mwfKTgpbfk= 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-600-3_KFZxFPPQ-w709ZgqxW9g-1; Tue, 21 Nov 2023 12:17:06 -0500 X-MC-Unique: 3_KFZxFPPQ-w709ZgqxW9g-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 4411988D01D for ; Tue, 21 Nov 2023 17:17:06 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.39.194.53]) by smtp.corp.redhat.com (Postfix) with ESMTPS id A4352492BFA; Tue, 21 Nov 2023 17:17:05 +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 3ALHH3Qe4130872 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Tue, 21 Nov 2023 18:17:03 +0100 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 3ALHH2LL4130871; Tue, 21 Nov 2023 18:17:02 +0100 Date: Tue, 21 Nov 2023 18:17:02 +0100 From: Jakub Jelinek To: Jason Merrill Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] c++, v3: 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.4.1 on 10.11.54.10 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline 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 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: , Reply-To: Jakub Jelinek Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1783194782624899346 X-GMAIL-MSGID: 1783194782624899346 On Thu, Oct 26, 2023 at 09:21:47PM -0400, Jason Merrill wrote: > On 9/18/23 13:21, Jakub Jelinek wrote: > > Here is an updated version of the patch. > > Compared to the last version, based on the discussion in the PR, the patch > > 1) warns (but only that) if size()/data() methods aren't declared > > constexpr/consteval (or implicitly constexpr) > > The language requirements also seem to be satisfied by Thanks, these 2 now work. Most of review comments incorporated. > > + 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"); > > This can use cxx_constant_value to show what makes it not a > constant-expression. And also don't assume size is a member function. In this case, I've split it, if !tree_fits_uhwi_p (message_sz) (as the value is known to be size_t typed) it really means it isn't constant expression, while the case when it is too large for host int is just a restriction we imply on it because we don't really support too large strings. Furthermore, I've used cxx_constant_value in addition to the messages (just removing "member function " part from the wording, and using [%d] for data or adding "core "). The reason is that the issues during constant expression evaluation are typically diagnosed at a different location and I think it is useful that people know both why it isn't a constant expression and during evaluation of what it happened. > > --- gcc/testsuite/g++.dg/cpp0x/udlit-error1.C.jj 2023-09-18 13:08:31.530448184 +0200 > > +++ gcc/testsuite/g++.dg/cpp0x/udlit-error1.C 2023-09-18 13:09:47.167440904 +0200 > > @@ -11,7 +11,8 @@ void operator""_x(const char *, decltype > > #pragma message "hi"_x // { dg-warning "string literal with user-defined suffix is invalid in this context" } > > extern "C"_x { void g(); } // { dg-error "before user-defined string literal" } > > -static_assert(true, "foo"_x); // { dg-error "string literal with user-defined suffix is invalid in this context|expected" } > > +static_assert(true, "foo"_x); // { dg-error "'static_assert' with non-string message only available with" "" { target c++23_down } } > > This diagnostic message seems unclear for a UDL? > > > + // { dg-error "'static_assert' message must be a string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' members" "" { target *-*-* } .-1 } The real diagnostic is this line, not the first one (which is just a pedwarn about trying to use something C++26 in older code). And I'm not really sure what to do about it. The static_assert (false, "foo"_myd); in the new testcase shows where it is valid (in C++26 and as extension in older standards). For C++26 we could use unevaluated string literal rather than string literal in the wording, but C++23 and earlier don't have that, so we would need to say something like non user-defined string literal without encoding prefix or object with 'size' and 'data' members. > > [[deprecated("oof"_x)]] // { dg-error "string literal with user-defined suffix is invalid in this context" "" { target c++26 } } > > void Anyway, here is the updated patch with all the changes, but nothing done about user-defined literals. Note, as the placeholder patch hasn't been reviewed, I've moved the -Wc++26-extensions hunks from that patch to this patch. 2023-11-21 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. * 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/doc/invoke.texi.jj 2023-11-21 09:31:36.008392269 +0100 +++ gcc/doc/invoke.texi 2023-11-21 15:39:52.448638303 +0100 @@ -9106,6 +9106,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-11 08:52:20.129849104 +0100 +++ gcc/c-family/c.opt 2023-11-21 15:39:52.859632548 +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-20 09:50:07.731214433 +0100 +++ gcc/c-family/c-cppbuiltin.cc 2023-11-21 15:39:30.042951889 +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-20 09:50:08.067209741 +0100 +++ gcc/cp/parser.cc 2023-11-21 15:31:39.366534804 +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-11 08:52:20.555843211 +0100 +++ gcc/cp/semantics.cc 2023-11-21 17:34:24.074374632 +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 @@ -11443,11 +11444,69 @@ 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); + message_data + = finish_class_member_access_expr (message, + get_identifier ("data"), + false, 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; + } + releasing_vec size_args, data_args; + message_sz = finish_call_expr (message_sz, &size_args, false, false, + tf_warning_or_error); + message_data = finish_call_expr (message_data, &data_args, false, false, + tf_warning_or_error); + if (message_sz == error_mark_node || message_data == error_mark_node) + return; + if (tree s + = cp_get_callee_fndecl_nofold (extract_call_expr (message_sz))) + if (!DECL_DECLARED_CONSTEXPR_P (s)) + warning_at (location, 0, "%qD used in % message " + "is not %", s); + message_sz = build_converted_constant_expr (size_type_node, message_sz, + tf_none); + if (message_sz == error_mark_node) + { + error_at (location, "% message % " + "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, "%qD used in % message " + "is not %", d); + message_data = build_converted_constant_expr (const_string_type_node, + message_data, tf_none); + 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 +11544,96 @@ 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 + = fold_non_dependent_expr (message_sz, complain, + /*manifestly_const_eval=*/true); + if (!tree_fits_uhwi_p (msz)) + { + cxx_constant_value (message_sz); + 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", message_sz); + return; + } + len = tree_to_uhwi (msz); + 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); + tree t2 + = fold_non_dependent_expr (t, complain, + /*manifestly_const_eval=*/ + true); + if (!tree_fits_shwi_p (t2)) + { + cxx_constant_value (t); + 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) + { + data = build2 (COMPOUND_EXPR, integer_type_node, + message_data, integer_zero_node); + tree t + = fold_non_dependent_expr (data, complain, + /*manifestly_const_eval=*/true); + if (!integer_zerop (t)) + { + cxx_constant_value (data); + 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 +11643,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-20 09:50:08.081209546 +0100 +++ gcc/cp/pt.cc 2023-11-21 15:31:39.425533979 +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-21 15:31:39.426533965 +0100 +++ gcc/testsuite/g++.dg/cpp26/static_assert1.C 2023-11-21 17:45:27.862086804 +0100 @@ -0,0 +1,300 @@ +// 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 "'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 } +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 } +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 "'short int H::size\\\(\\\) const' used in 'static_assert' message is 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 "'const char\\\* I::data\\\(\\\) const' used in 'static_assert' message is not 'constexpr'" "" { target *-*-* } .-1 } +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 } +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 } + 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-09-19 09:24:20.921882354 +0200 +++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 2023-11-21 15:31:39.447533671 +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-02 07:49:18.265848989 +0100 +++ gcc/testsuite/g++.dg/cpp0x/udlit-error1.C 2023-11-21 15:31:39.470533350 +0100 @@ -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