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 D64F73858D35 for ; Tue, 21 Nov 2023 21:44:07 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D64F73858D35 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 D64F73858D35 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=1700603053; cv=none; b=emwmTxHcRKtqYZv3UIeOyFNcDfm59Cu9CkB3Zumo7naiYU6hzWMtSstwwWB4QiiLFApj09O6xmGZbPTeQ1OgkeiL7uYzGkuCT4pWJfkYBXpaZ1I4DSMiwjvpDEU3e3wqsMwNBq6AJgCETzbB3hRLyAbRCY7ayzozGsMbqNVg8sk= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700603053; c=relaxed/simple; bh=WgPMC7w4+49HkTM0Ege0Hl14/VUy6CASk6Z1HJsrsM0=; h=DKIM-Signature:Message-ID:Date:MIME-Version:Subject:To:From; b=pshFi0vUWIQhmRFnw4Z8zK1EbzLRI+NMCnHtQCpGgWKGd60qLIYY8fBLhV+KVRVLUbCuT1uaocLGXQzJNwTwAPv69zft7m0FHKdyZjCxQa7jjOEILPBR06VVFHVYY8yBk+/0J+8l4fv+uPzWmyAQ/78eGtUemzjmMEfb5l2D25k= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700603047; h=from:from: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=O3E26Bij76VuU9kdWdiYpjQENed6xE3X8mVaoLjJf+o=; b=LBx9lp7m5pKuL68wv0mUGuuzAfhIa0KtLn7EaeEntwt+g0B5isg9rgArZXBAaB0tOBJLdU E5DyqBpAPUoklhcfbn7H3jDxI861hCCc5S0rWQq3bya3smS05at2pQRzQyFEgtj3rgAXVL KoA0iF+sqAKrmlrDR6Uve0iY2rol0DM= Received: from mail-oa1-f71.google.com (mail-oa1-f71.google.com [209.85.160.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-46-m00bhGADMVOkkMusaMcnMg-1; Tue, 21 Nov 2023 16:44:04 -0500 X-MC-Unique: m00bhGADMVOkkMusaMcnMg-1 Received: by mail-oa1-f71.google.com with SMTP id 586e51a60fabf-1f938410ae2so2954213fac.3 for ; Tue, 21 Nov 2023 13:44:04 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700603044; x=1701207844; h=content-transfer-encoding:in-reply-to:from:references:cc:to :content-language:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=O3E26Bij76VuU9kdWdiYpjQENed6xE3X8mVaoLjJf+o=; b=Qjabha9bQD6eEZIUGw6/khp12KPN/pqeG+2zmlcQXlCLejUoNWJuXcqDBaY2QM8KxH ffuKmLMZHjUgwAMLzm61Ogddpq4mxVSulCjzDPJ6JhiTaCvZ3AxVpBFwKI4jcXjSXXQB vROHWtON1tAl8azAjH/Ev7IALXt2uQhoL72cW1q7zKq8I3nTNNm1AB1kXSxX8ymuTDlU 2RKDWSL+QmpePri1NJ5DF1+e+GFArWNHmxLtVy1sT4CbhVpg3caAYtTZ/nA2EW4j5afY DNJbzlVrRJsoPp+OVuV8flCmi7Gvzs4rx7GoWUYHV3ixCYpKOCwxbW2TrBUOhfkVfp0t RXbA== X-Gm-Message-State: AOJu0YzhZukoOL/oK7yWa1lGtBBPmtDh22h/5MsThYE+lBi2wZyUoezz ImtLL8REUR0domZLqlfV0/lU5TkOcYWO7aXXFjz+lcpdGMahHy1eO7s7/gueP8GsVa0L6trhRL7 m7oE5J9PQy5vS6qZ40w== X-Received: by 2002:a05:6871:431b:b0:1f5:2b0c:706b with SMTP id lu27-20020a056871431b00b001f52b0c706bmr710295oab.28.1700603043705; Tue, 21 Nov 2023 13:44:03 -0800 (PST) X-Google-Smtp-Source: AGHT+IE0OnHygw3E1r4U0wByKm9HnLugAM8haxo5WYvq5qc4dYK24MuxkQIv0zY00DPqwtwd/7dTDQ== X-Received: by 2002:a05:6871:431b:b0:1f5:2b0c:706b with SMTP id lu27-20020a056871431b00b001f52b0c706bmr710262oab.28.1700603042948; Tue, 21 Nov 2023 13:44:02 -0800 (PST) Received: from [192.168.1.145] (130-44-146-16.s12558.c3-0.arl-cbr1.sbo-arl.ma.cable.rcncustomer.com. [130.44.146.16]) by smtp.gmail.com with ESMTPSA id j12-20020ac8550c000000b0041520676966sm3931975qtq.47.2023.11.21.13.44.02 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 21 Nov 2023 13:44:02 -0800 (PST) Message-ID: <4b133eec-f806-43af-800a-8339d50645fd@redhat.com> Date: Tue, 21 Nov 2023 16:44:01 -0500 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH] c++, v3: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348] To: Jakub Jelinek Cc: gcc-patches@gcc.gnu.org References: From: Jason Merrill In-Reply-To: X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-5.3 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,RCVD_IN_SORBS_WEB,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=no 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 11/21/23 12:17, Jakub Jelinek wrote: > 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. Agreed. >>> --- 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. > Or do you want to just use > error_at (location, "% message must be a " > "unevaluated string literal or object with " > "% and % members"); > wording (even when it is in C++26 term) regardless of the -std= level? No, I think "unevaluated string literal" will be confusing to users. I guess it's fine as it is, let's just print the type (as commented inline below). >>> [[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. > > --- 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; This seems asymmetric, let's return for bare packs in the condition as well. > + 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"); Let's print the type of the message as well. > + 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); I don't think we need this check, it should be covered by the later constant-expression checks. > + 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 " > + "%"); Let's also print the type of size(). > + 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); Let's also drop this check. > + 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 " > + "%"); And print this type. > + 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); We can call cxx_constant_value here instead of fold_non_dependent_expr, since we don't get here in a template. > + 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); And this can be maybe_constant_value. > + 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); This can also be cxx_constant_value. > + 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) > + { Let's add a comment here about how you're using (data(), 0) to test core-constant. > + 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 > > > Jakub >