From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 177543858D1E for ; Fri, 30 Sep 2022 20:39:31 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 177543858D1E Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1664570370; 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=aF/Mkus1k35xI5Psm0pDrDeQEkCTIr34uUCLczjiR8Y=; b=O+gVdClWkdKwR4+47XaJHBaAVrjgKDn/pP8VbMNipOcKRPhMB0rP8HVjNp8VMCIElAsN2S nJJ5eJn7YCchk4WE+1SldaQbtJJ+HJzXvpkSRXmiUvnwtgl6zi4rOQpBdEs8BdYyFrMX1D AMQ8lBnonSbEbD33/85POeUwrf43258= Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-359-ToT19Ve1MOC_jgrVfXwwZg-1; Fri, 30 Sep 2022 16:39:29 -0400 X-MC-Unique: ToT19Ve1MOC_jgrVfXwwZg-1 Received: by mail-qt1-f197.google.com with SMTP id fx6-20020a05622a4ac600b0035a70ba1cbcso3746586qtb.21 for ; Fri, 30 Sep 2022 13:39:28 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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; bh=aF/Mkus1k35xI5Psm0pDrDeQEkCTIr34uUCLczjiR8Y=; b=PQ9PBjgYs3ghSu+50VnBaII4PPO0df2qbpQIwiy/zns1nDf4/U2tCY52uxmpwqLGaK 2xLQ2D7N9Phzylvb4BMRTkWwIagLLLTLEspmvAYpnziLrSBCTVQxDMoSwR07+ZppUDIP 7fQGYnhwztRPg/L2mq4jIZ2btcD6+zI3GEioIMyvQrn6BUACUVIimL8O44myX86r2ehc oW5Xus2qxoUaNsbhcz2WqoRtbPoV/eU4Thd7ke1HSVfwGzqwn6yv5Z+FWDMC2BwUATkT iCqw+U7jopiylujdRhsSPgNr1Avj4uopY3/jGdLTcNhPq4vkuIdQGDiSQbJeglkie8rY Lmkg== X-Gm-Message-State: ACrzQf2wUg1yjV9WqI9VNU7j+qRKkx1KqGkIIcnKxAAJBKY4/RwvJqp0 llSg16pDi924lG8hGYuKURLZ+xl+k0yMkYhRbAsoC3rKUgbzyISHQr/VmDxjrmf7wPlecY8jgXH b5U0OpyScJS/zG1+dBA== X-Received: by 2002:a05:620a:4919:b0:6cf:5a4c:e3a3 with SMTP id ed25-20020a05620a491900b006cf5a4ce3a3mr7420881qkb.309.1664570367447; Fri, 30 Sep 2022 13:39:27 -0700 (PDT) X-Google-Smtp-Source: AMsMyM7GZc7Zmj5wvlOSHbqMfOPYPqPBtYChTWRzOwa4xTLaMH0iCSuRSGt4gL9ZToyIKjvBL5FldQ== X-Received: by 2002:a05:620a:4919:b0:6cf:5a4c:e3a3 with SMTP id ed25-20020a05620a491900b006cf5a4ce3a3mr7420846qkb.309.1664570366429; Fri, 30 Sep 2022 13:39:26 -0700 (PDT) Received: from [192.168.1.101] (130-44-159-43.s15913.c3-0.arl-cbr1.sbo-arl.ma.cable.rcncustomer.com. [130.44.159.43]) by smtp.gmail.com with ESMTPSA id y4-20020a37f604000000b006bbc09af9f5sm3258347qkj.101.2022.09.30.13.39.25 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 30 Sep 2022 13:39:25 -0700 (PDT) Message-ID: <8b360a15-5255-3fbe-16f4-84eaf0fa9612@redhat.com> Date: Fri, 30 Sep 2022 16:39:25 -0400 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.13.1 Subject: Re: [PATCH] c++, c: Implement C++23 P1774R8 - Portable assumptions [PR106654] 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: 7bit X-Spam-Status: No, score=-7.3 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,KAM_SHORT,NICE_REPLY_A,RCVD_IN_DNSWL_LOW,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On 9/22/22 05:55, Jakub Jelinek wrote: > Hi! > > The following patch implements C++23 P1774R8 - Portable assumptions > paper, by introducing support for [[assume (cond)]]; attribute for C++. > In addition to that the patch adds [[gnu::assume (cond)]]; and > __attribute__((assume (cond))); support to both C and C++. > As described in C++23, the attribute argument is conditional-expression > rather than the usual assignment-expression for attribute arguments, > the condition is contextually converted to bool (for C truthvalue conversion > is done on it) and is never evaluated at runtime. > For C++ constant expression evaluation, I only check the simplest conditions > for undefined behavior, because otherwise I'd need to undo changes to > *ctx->global which happened during the evaluation (but I believe the spec > allows that and we can further improve later). > The patch uses a new internal function, .ASSUME, to hold the condition > in the FEs. At gimplification time, if the condition is simple/without > side-effects, it is gimplified as if (cond) ; else __builtin_unreachable (); > and otherwise for now dropped on the floor. The intent is to incrementally > outline the conditions into separate artificial functions and use > .ASSUME further to tell the ranger and perhaps other optimization passes > about the assumptions, as detailed in the PR. > > When implementing it, I found that assume entry hasn't been added to > https://eel.is/c++draft/cpp.cond#6 > Jonathan said he'll file a NB comment about it, this patch assumes it > has been added into the table as 202207L when the paper has been voted in. > > With the attributes for both C/C++, I'd say we don't need to add > __builtin_assume with similar purpose, especially when __builtin_assume > in LLVM is just weird. It is strange for side-effects in function call's > argument not to be evaluated, and LLVM in that case (annoyingly) warns > and ignores the side-effects (but doesn't do then anything with it), > if there are no side-effects, it will work like our > if (!cond) __builtin_unreachable (); > > During bootstrap/regtest, I've discovered a problem with the way we > handle scoped attributes. For declaration or type attributes for attributes > we don't know anything about we just don't add them to the declarations or > types, so later in the FEs and middle-end it is fine to use lookup_attribute > etc. which just check the attribute name and not namespace because > non-standard non-GNU attributes just won't show there. But in the case of > attributes on statements, nothing has filtered out the unknown attributes, > so with my earlier patch e.g. c-c++-common/Wno-attributes-6.c test failed > because it uses: > [[vendor::assume(1 + 1 == 2)]]; > with -Wno-attributes=vendor::assume and lookup_attribute ("assume", ) > finds such attribute and handled it that way. > So, for those cases, this patch introduces lookup_attribute and > remove_attribute overloads which specify also the namespace. > I think the fallthrough, hot, cold, likely, unlikely attribute handling > will need to use the new APIs too, so that we don't handle > msft::fallthrough attribute as something we'd know. Sounds good. > Earlier version (without the attribs.{h,cc} changes and the 3 argument > lookup_attribute/remove_attribute uses instead of 2) has been successfully > bootstrapped/regtested on x86_64-linux and i686-linux with the > FAIL: c-c++-common/Wno-attributes-6.c -Wc++-compat (test for excess errors) > regression, ok for trunk if this passes full bootstrap/regtest again > (note, I've of course checked it already with > GXX_TESTSUITE_STDS=98,11,14,17,20,2b make check-gcc check-g++ \ > RUNTESTFLAGS="dg.exp='feat* attr-assume* Wno-attrib*'" > )? > > 2022-09-22 Jakub Jelinek > > PR c++/106654 > gcc/ > * internal-fn.def (ASSUME): New internal function. > * internal-fn.h (expand_ASSUME): Declare. > * internal-fn.cc (expand_ASSUME): Define. > * gimplify.cc (gimplify_call_expr): Gimplify IFN_ASSUME. > * fold-const.h (simple_operand_p_2): Declare. This needs a better name if it's going to be a public interface. The usage also needs rationale for why this is the right predicate for assume, rather than just no-side-effects. Surely the latter is right for constexpr, at least? > * fold-const.cc (simple_operand_p_2): Remove forward declaration. > No longer static. Adjust function comment and fix a typo in it. > (simple_operand_p): Adjust function comment. > * attribs.h (remove_attribute): Declare overload with additional > attr_ns argument. > (private_lookup_attribute): Declare overload with additional > attr_ns and attr_ns_len arguments. > (lookup_attribute): New overload with additional attr_ns argument. > * attribs.cc (remove_attribute): New overload with additional > attr_ns argument. > (private_lookup_attribute): New overload with additional > attr_ns and attr_ns_len arguments. > * doc/extend.texi: Document assume attribute. Move fallthrough > attribute example to its section. > gcc/c-family/ > * c-attribs.cc (handle_assume_attribute): New function. > (c_common_attribute_table): Add entry for assume attribute. > * c-lex.cc (c_common_has_attribute): Handle > __have_cpp_attribute (assume). > gcc/c/ > * c-parser.cc (handle_assume_attribute): New function. > (c_parser_declaration_or_fndef): Handle assume attribute. > (c_parser_attribute_arguments): Add assume_attr argument, > if true, parse first argument as conditional expression. > (c_parser_gnu_attribute, c_parser_std_attribute): Adjust > c_parser_attribute_arguments callers. > (c_parser_statement_after_labels) : Handle > assume attribute. > gcc/cp/ > * cp-tree.h (process_stmt_assume_attribute): Implement C++23 > P1774R8 - Portable assumptions. Declare. > * name-lookup.h (enum scope_kind): Add sk_assume. > * parser.cc (assume_attr): New enumerator. > (cp_parser_parenthesized_expression_list): Handle assume_attr. > (cp_parser_statement): Handle assume attribute. > (cp_parser_expression_statement): Likewise. > (cp_parser_gnu_attribute_list): Use assume_attr for assume > attribute. > (cp_parser_std_attribute): Likewise. Handle standard assume > attribute like gnu::assume. > * cp-gimplify.cc (process_stmt_assume_attribute): New function. > * decl.cc (struct named_label_entry): Add in_assume member. > (poplevel_named_label_1): Set it. > (check_previous_goto_1): Diagnose entering assume attribute > condition. > (check_goto): Likewise. > * constexpr.cc: Include fold-const.h. > (cxx_eval_internal_function): Handle IFN_ASSUME. > (potential_constant_expression_1): Likewise. > * pt.cc (tsubst_copy_and_build): Likewise. > gcc/testsuite/ > * gcc.dg/attr-assume-1.c: New test. > * gcc.dg/attr-assume-2.c: New test. > * gcc.dg/attr-assume-3.c: New test. > * g++.dg/cpp2a/feat-cxx2a.C: Add colon to C++20 features > comment, add C++20 attributes comment and move C++20 > new features after the attributes before them. > * g++.dg/cpp23/feat-cxx2b.C: Likewise. Test > __has_cpp_attribute(assume). > * g++.dg/cpp23/attr-assume1.C: New test. > * g++.dg/cpp23/attr-assume2.C: New test. > * g++.dg/cpp23/attr-assume3.C: New test. > * g++.dg/cpp23/attr-assume4.C: New test. > * g++.dg/cpp23/attr-assume5.C: New test. > > --- gcc/internal-fn.def.jj 2022-09-22 00:14:19.568087771 +0200 > +++ gcc/internal-fn.def 2022-09-22 00:24:00.502186674 +0200 > @@ -462,6 +462,10 @@ DEF_INTERNAL_FN (TRAP, ECF_CONST | ECF_L > | ECF_NOTHROW | ECF_COLD | ECF_LOOPING_CONST_OR_PURE, > NULL) > > +/* [[assume (cond)]]. */ > +DEF_INTERNAL_FN (ASSUME, ECF_CONST | ECF_LEAF | ECF_NOTHROW > + | ECF_LOOPING_CONST_OR_PURE, NULL) > + > #undef DEF_INTERNAL_INT_FN > #undef DEF_INTERNAL_FLT_FN > #undef DEF_INTERNAL_FLT_FLOATN_FN > --- gcc/internal-fn.h.jj 2022-09-22 00:14:19.605087267 +0200 > +++ gcc/internal-fn.h 2022-09-22 00:24:00.502186674 +0200 > @@ -243,6 +243,7 @@ extern void expand_PHI (internal_fn, gca > extern void expand_SHUFFLEVECTOR (internal_fn, gcall *); > extern void expand_SPACESHIP (internal_fn, gcall *); > extern void expand_TRAP (internal_fn, gcall *); > +extern void expand_ASSUME (internal_fn, gcall *); > > extern bool vectorized_internal_fn_supported_p (internal_fn, tree); > > --- gcc/internal-fn.cc.jj 2022-09-22 00:14:19.550088015 +0200 > +++ gcc/internal-fn.cc 2022-09-22 00:24:00.910181125 +0200 > @@ -4508,3 +4508,9 @@ expand_TRAP (internal_fn, gcall *) > { > expand_builtin_trap (); > } > + > +void > +expand_ASSUME (internal_fn, gcall *) > +{ > + gcc_unreachable (); > +} > --- gcc/gimplify.cc.jj 2022-09-22 00:14:19.391090178 +0200 > +++ gcc/gimplify.cc 2022-09-22 00:24:00.954180527 +0200 > @@ -3554,6 +3554,25 @@ gimplify_call_expr (tree *expr_p, gimple > enum internal_fn ifn = CALL_EXPR_IFN (*expr_p); > auto_vec vargs (nargs); > > + if (ifn == IFN_ASSUME) > + { > + if (simple_operand_p_2 (CALL_EXPR_ARG (*expr_p, 0))) > + { > + /* If the [[assume (cond)]]; condition is simple > + enough and can be evaluated unconditionally > + without side-effects, expand it as > + if (!cond) __builtin_unreachable (); */ > + tree fndecl = builtin_decl_explicit (BUILT_IN_UNREACHABLE); > + *expr_p = build3 (COND_EXPR, void_type_node, > + CALL_EXPR_ARG (*expr_p, 0), void_node, > + build_call_expr_loc (EXPR_LOCATION (*expr_p), > + fndecl, 0)); > + return GS_OK; > + } > + /* FIXME: Otherwise expand it specially. */ > + return GS_ALL_DONE; > + } > + > for (i = 0; i < nargs; i++) > { > gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p, > --- gcc/fold-const.h.jj 2022-09-22 00:14:19.325091076 +0200 > +++ gcc/fold-const.h 2022-09-22 00:24:00.955180513 +0200 > @@ -215,6 +215,7 @@ extern tree build_range_check (location_ > extern bool merge_ranges (int *, tree *, tree *, int, tree, tree, int, > tree, tree); > extern tree sign_bit_p (tree, const_tree); > +extern bool simple_operand_p_2 (tree); > extern tree exact_inverse (tree, tree); > extern bool expr_not_equal_to (tree t, const wide_int &); > extern tree const_unop (enum tree_code, tree, tree); > --- gcc/fold-const.cc.jj 2022-09-22 00:14:19.313091239 +0200 > +++ gcc/fold-const.cc 2022-09-22 00:24:00.990180037 +0200 > @@ -130,7 +130,6 @@ static tree eval_subst (location_t, tree > static tree optimize_bit_field_compare (location_t, enum tree_code, > tree, tree, tree); > static bool simple_operand_p (const_tree); > -static bool simple_operand_p_2 (tree); > static tree range_binop (enum tree_code, tree, tree, int, tree, int); > static tree range_predecessor (tree); > static tree range_successor (tree); > @@ -4868,8 +4867,8 @@ sign_bit_p (tree exp, const_tree val) > return NULL_TREE; > } > > -/* Subroutine for fold_truth_andor_1: determine if an operand is simple enough > - to be evaluated unconditionally. */ > +/* Subroutine for fold_truth_andor_1 and simple_operand_p_2: determine if an > + operand is simple enough to be evaluated unconditionally. */ > > static bool > simple_operand_p (const_tree exp) > @@ -4897,12 +4896,11 @@ simple_operand_p (const_tree exp) > && (! TREE_STATIC (exp) || DECL_REGISTER (exp)))); > } > > -/* Subroutine for fold_truth_andor: determine if an operand is simple enough > - to be evaluated unconditionally. > - I addition to simple_operand_p, we assume that comparisons, conversions, > +/* Determine if an operand is simple enough to be evaluated unconditionally. > + In addition to simple_operand_p, we assume that comparisons, conversions, > and logic-not operations are simple, if their operands are simple, too. */ > > -static bool > +bool > simple_operand_p_2 (tree exp) > { > enum tree_code code; > --- gcc/attribs.h.jj 2022-01-11 23:11:21.543302056 +0100 > +++ gcc/attribs.h 2022-09-22 10:50:46.299971495 +0200 > @@ -82,6 +82,10 @@ extern tree merge_type_attributes (tree, > > extern tree remove_attribute (const char *, tree); > > +/* Similarly but also with specific attribute namespace. */ > + > +extern tree remove_attribute (const char *, const char *, tree); > + > /* Given two attributes lists, return a list of their union. */ > > extern tree merge_attributes (tree, tree); > @@ -113,6 +117,10 @@ extern int attribute_list_contained (con > for size. */ > extern tree private_lookup_attribute (const char *attr_name, size_t attr_len, > tree list); > +extern tree private_lookup_attribute (const char *attr_ns, > + const char *attr_name, > + size_t attr_ns_len, size_t attr_len, > + tree list); > > extern unsigned decls_mismatched_attributes (tree, tree, tree, > const char* const[], > @@ -209,6 +217,36 @@ lookup_attribute (const char *attr_name, > } > } > > +/* Similar to lookup_attribute, but also match the attribute namespace. */ > + > +static inline tree > +lookup_attribute (const char *attr_ns, const char *attr_name, tree list) > +{ > + if (CHECKING_P && attr_name[0] != '_') > + { > + size_t attr_len = strlen (attr_name); > + gcc_checking_assert (!canonicalize_attr_name (attr_name, attr_len)); > + } > + if (CHECKING_P && attr_ns && attr_ns[0] != '_') > + { > + size_t attr_ns_len = strlen (attr_ns); > + gcc_checking_assert (!canonicalize_attr_name (attr_ns, attr_ns_len)); > + } > + /* In most cases, list is NULL_TREE. */ > + if (list == NULL_TREE) > + return NULL_TREE; > + else > + { > + size_t attr_ns_len = attr_ns ? strlen (attr_ns) : 0; > + size_t attr_len = strlen (attr_name); > + /* Do the strlen() before calling the out-of-line implementation. > + In most cases attr_name is a string constant, and the compiler > + will optimize the strlen() away. */ > + return private_lookup_attribute (attr_ns, attr_name, > + attr_ns_len, attr_len, list); > + } > +} > + > /* Given an attribute name ATTR_NAME and a list of attributes LIST, > return a pointer to the attribute's list first element if the attribute > starts with ATTR_NAME. ATTR_NAME must be in the form 'text' (not > --- gcc/attribs.cc.jj 2022-05-17 09:01:11.988655748 +0200 > +++ gcc/attribs.cc 2022-09-22 10:54:44.693705319 +0200 > @@ -1639,6 +1639,36 @@ remove_attribute (const char *attr_name, > return list; > } > > +/* Similarly but also match namespace on the removed attributes. */ > + > +tree > +remove_attribute (const char *attr_ns, const char *attr_name, tree list) > +{ > + tree *p; > + gcc_checking_assert (attr_name[0] != '_'); > + gcc_checking_assert (attr_ns == NULL || attr_ns[0] != '_'); > + > + for (p = &list; *p;) > + { > + tree l = *p; > + > + tree attr = get_attribute_name (l); > + if (is_attribute_p (attr_name, attr)) > + { > + tree ns = get_attribute_namespace (l); > + if ((ns == NULL_TREE && attr_ns == NULL) > + || (ns && attr_ns && is_attribute_p (attr_ns, ns))) > + { > + *p = TREE_CHAIN (l); > + continue; > + } > + } > + p = &TREE_CHAIN (l); > + } > + > + return list; > +} > + > /* Return an attribute list that is the union of a1 and a2. */ > > tree > @@ -2033,6 +2063,39 @@ private_lookup_attribute (const char *at > list = TREE_CHAIN (list); > } > > + return list; > +} > + > +/* Similarly but with also attribute namespace. */ > + > +tree > +private_lookup_attribute (const char *attr_ns, const char *attr_name, > + size_t attr_ns_len, size_t attr_len, tree list) > +{ > + while (list) > + { > + tree attr = get_attribute_name (list); > + size_t ident_len = IDENTIFIER_LENGTH (attr); > + if (cmp_attribs (attr_name, attr_len, IDENTIFIER_POINTER (attr), > + ident_len)) > + { > + tree ns = get_attribute_namespace (list); > + if (ns == NULL_TREE) > + { > + if (attr_ns == NULL) > + break; > + } > + else if (attr_ns) > + { > + ident_len = IDENTIFIER_LENGTH (ns); > + if (cmp_attribs (attr_ns, attr_ns_len, IDENTIFIER_POINTER (ns), > + ident_len)) > + break; > + } > + } > + list = TREE_CHAIN (list); > + } > + > return list; > } > > --- gcc/doc/extend.texi.jj 2022-09-22 00:14:19.265091892 +0200 > +++ gcc/doc/extend.texi 2022-09-22 00:24:01.048179249 +0200 > @@ -9187,6 +9187,20 @@ available for functions (@pxref{Function > (@pxref{Variable Attributes}), labels (@pxref{Label Attributes}), enumerators > (@pxref{Enumerator Attributes}), and for types (@pxref{Type Attributes}). > > +@table @code > +@item fallthrough > +@cindex @code{fallthrough} statement attribute > +The @code{fallthrough} attribute with a null statement serves as a > +fallthrough statement. It hints to the compiler that a statement > +that falls through to another case label, or user-defined label > +in a switch statement is intentional and thus the > +@option{-Wimplicit-fallthrough} warning must not trigger. The > +fallthrough attribute may appear at most once in each attribute > +list, and may not be mixed with other attributes. It can only > +be used in a switch statement (the compiler will issue an error > +otherwise), after a preceding statement and before a logically > +succeeding case label, or user-defined label. > + > This example uses the @code{fallthrough} statement attribute to indicate that > the @option{-Wimplicit-fallthrough} warning should not be emitted: > > @@ -9201,19 +9215,28 @@ switch (cond) > @} > @end smallexample > > -@table @code > -@item fallthrough > -@cindex @code{fallthrough} statement attribute > -The @code{fallthrough} attribute with a null statement serves as a > -fallthrough statement. It hints to the compiler that a statement > -that falls through to another case label, or user-defined label > -in a switch statement is intentional and thus the > -@option{-Wimplicit-fallthrough} warning must not trigger. The > -fallthrough attribute may appear at most once in each attribute > -list, and may not be mixed with other attributes. It can only > -be used in a switch statement (the compiler will issue an error > -otherwise), after a preceding statement and before a logically > -succeeding case label, or user-defined label. > +@item assume > +@cindex @code{assume} statement attribute > +The @code{assume} attribute with a null statement serves as portable > +assumption. It should have a single argument, a conditional expression, > +which is not evaluated. If the argument would evaluate to true > +at the point where it appears, it has no effect, otherwise there > +is undefined behavior. This is a GNU variant of the ISO C++23 > +standard @code{assume} attribute, but it can be used in any version of > +both C and C++. > + > +@smallexample > +int > +foo (int x, int y) > +@{ > + __attribute__((assume(x == 42))); > + __attribute__((assume(++y == 43))); > + return x + y; > +@} > +@end smallexample > + > +@code{y} is not actually incremented and the compiler can but does not > +have to optimize it to just @code{return 42 + 42;}. > > @end table > > --- gcc/c-family/c-attribs.cc.jj 2022-09-22 00:14:18.695099644 +0200 > +++ gcc/c-family/c-attribs.cc 2022-09-22 00:24:01.074178895 +0200 > @@ -144,6 +144,7 @@ static tree handle_type_generic_attribut > static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *); > static tree handle_alloc_align_attribute (tree *, tree, tree, int, bool *); > static tree handle_assume_aligned_attribute (tree *, tree, tree, int, bool *); > +static tree handle_assume_attribute (tree *, tree, tree, int, bool *); > static tree handle_target_attribute (tree *, tree, tree, int, bool *); > static tree handle_target_clones_attribute (tree *, tree, tree, int, bool *); > static tree handle_optimize_attribute (tree *, tree, tree, int, bool *); > @@ -530,6 +531,8 @@ const struct attribute_spec c_common_att > handle_designated_init_attribute, NULL }, > { "fallthrough", 0, 0, false, false, false, false, > handle_fallthrough_attribute, NULL }, > + { "assume", 1, 1, false, false, false, false, > + handle_assume_attribute, NULL }, > { "patchable_function_entry", 1, 2, true, false, false, false, > handle_patchable_function_entry_attribute, > NULL }, > @@ -5738,6 +5741,18 @@ handle_fallthrough_attribute (tree *, tr > { > pedwarn (input_location, OPT_Wattributes, "%qE attribute ignored", name); > *no_add_attrs = true; > + return NULL_TREE; > +} > + > +/* Handle a "assume" attribute; arguments as in struct > + attribute_spec.handler. */ > + > +tree > +handle_assume_attribute (tree *, tree name, tree, int, > + bool *no_add_attrs) > +{ > + pedwarn (input_location, OPT_Wattributes, "%qE attribute ignored", name); > + *no_add_attrs = true; > return NULL_TREE; > } > > --- gcc/c-family/c-lex.cc.jj 2022-09-22 00:14:18.743098991 +0200 > +++ gcc/c-family/c-lex.cc 2022-09-22 00:24:01.074178895 +0200 > @@ -378,6 +378,8 @@ c_common_has_attribute (cpp_reader *pfil > result = 201803; > else if (is_attribute_p ("nodiscard", attr_name)) > result = 201907; > + else if (is_attribute_p ("assume", attr_name)) > + result = 202207; > } > else > { > --- gcc/c/c-parser.cc.jj 2022-09-22 00:14:18.798098243 +0200 > +++ gcc/c/c-parser.cc 2022-09-22 11:20:07.661847350 +0200 > @@ -1808,6 +1808,46 @@ add_debug_begin_stmt (location_t loc) > add_stmt (stmt); > } > > +/* Helper function for c_parser_declaration_or_fndef and > + Handle assume attribute(s). */ > + > +static tree > +handle_assume_attribute (location_t here, tree attrs, bool nested) > +{ > + if (nested) > + for (tree attr = lookup_attribute ("gnu", "assume", attrs); attr; > + attr = lookup_attribute ("gnu", "assume", TREE_CHAIN (attr))) > + { > + tree args = TREE_VALUE (attr); > + int nargs = list_length (args); > + if (nargs != 1) > + { > + error_at (here, "wrong number of arguments specified " > + "for %qE attribute", > + get_attribute_name (attr)); > + inform (here, "expected %i, found %i", 1, nargs); > + } > + else > + { > + tree arg = TREE_VALUE (args); > + arg = c_objc_common_truthvalue_conversion (here, arg); > + arg = c_fully_fold (arg, false, NULL); > + if (arg != error_mark_node) > + { > + tree fn = build_call_expr_internal_loc (here, IFN_ASSUME, > + void_type_node, 1, > + arg); > + add_stmt (fn); > + } > + } > + } > + else > + pedwarn (here, OPT_Wattributes, > + "% attribute at top level"); > + > + return remove_attribute ("gnu", "assume", attrs); > +} > + > /* Parse a declaration or function definition (C90 6.5, 6.7.1, C99 > 6.7, 6.9.1, C11 6.7, 6.9.1). If FNDEF_OK is true, a function definition > is accepted; otherwise (old-style parameter declarations) only other > @@ -2022,6 +2062,14 @@ c_parser_declaration_or_fndef (c_parser > bool auto_type_p = specs->typespec_word == cts_auto_type; > if (c_parser_next_token_is (parser, CPP_SEMICOLON)) > { > + bool handled_assume = false; > + if (specs->typespec_kind == ctsk_none > + && lookup_attribute ("gnu", "assume", specs->attrs)) > + { > + handled_assume = true; > + specs->attrs > + = handle_assume_attribute (here, specs->attrs, nested); > + } > if (auto_type_p) > error_at (here, "%<__auto_type%> in empty declaration"); > else if (specs->typespec_kind == ctsk_none > @@ -2039,13 +2087,15 @@ c_parser_declaration_or_fndef (c_parser > pedwarn (here, OPT_Wattributes, > "% attribute at top level"); > } > - else if (empty_ok && !(have_attrs > - && specs->non_std_attrs_seen_p)) > + else if (empty_ok > + && !(have_attrs && specs->non_std_attrs_seen_p) > + && !handled_assume) > shadow_tag (specs); > else > { > shadow_tag_warned (specs, 1); > - pedwarn (here, 0, "empty declaration"); > + if (!handled_assume) > + pedwarn (here, 0, "empty declaration"); > } > c_parser_consume_token (parser); > if (oacc_routine_data) > @@ -2145,6 +2195,9 @@ c_parser_declaration_or_fndef (c_parser > else if (attribute_fallthrough_p (specs->attrs)) > warning_at (here, OPT_Wattributes, > "% attribute not followed by %<;%>"); > + else if (lookup_attribute ("gnu", "assume", specs->attrs)) > + warning_at (here, OPT_Wattributes, > + "% attribute not followed by %<;%>"); > > pending_xref_error (); > prefix_attrs = specs->attrs; > @@ -4583,7 +4636,8 @@ c_parser_gnu_attribute_any_word (c_parse > > static tree > c_parser_attribute_arguments (c_parser *parser, bool takes_identifier, > - bool require_string, bool allow_empty_args) > + bool require_string, bool assume_attr, > + bool allow_empty_args) > { > vec *expr_list; > tree attr_args; > @@ -4602,6 +4656,7 @@ c_parser_attribute_arguments (c_parser * > == CPP_CLOSE_PAREN)) > && (takes_identifier > || (c_dialect_objc () > + && !assume_attr > && c_parser_peek_token (parser)->id_kind > == C_ID_CLASSNAME))) > { > @@ -4638,6 +4693,23 @@ c_parser_attribute_arguments (c_parser * > tree string = c_parser_string_literal (parser, false, true).value; > attr_args = build_tree_list (NULL_TREE, string); > } > + else if (assume_attr) > + { > + tree cond > + = c_parser_conditional_expression (parser, NULL, NULL_TREE).value; > + if (!c_parser_next_token_is (parser, CPP_COMMA)) > + attr_args = build_tree_list (NULL_TREE, cond); > + else > + { > + tree tree_list; > + c_parser_consume_token (parser); > + expr_list = c_parser_expr_list (parser, false, true, > + NULL, NULL, NULL, NULL); > + tree_list = build_tree_list_vec (expr_list); > + attr_args = tree_cons (NULL_TREE, cond, tree_list); > + release_tree_vector (expr_list); > + } > + } > else > { > expr_list = c_parser_expr_list (parser, false, true, > @@ -4721,7 +4793,9 @@ c_parser_gnu_attribute (c_parser *parser > tree attr_args > = c_parser_attribute_arguments (parser, > attribute_takes_identifier_p (attr_name), > - false, true); > + false, > + is_attribute_p ("assume", attr_name), > + true); > > attr = build_tree_list (attr_name, attr_args); > if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) > @@ -4967,9 +5041,13 @@ c_parser_std_attribute (c_parser *parser > = (ns == NULL_TREE > && (strcmp (IDENTIFIER_POINTER (name), "deprecated") == 0 > || strcmp (IDENTIFIER_POINTER (name), "nodiscard") == 0)); > + bool assume_attr > + = (ns != NULL_TREE > + && strcmp (IDENTIFIER_POINTER (ns), "gnu") == 0 > + && strcmp (IDENTIFIER_POINTER (name), "assume") == 0); > TREE_VALUE (attribute) > = c_parser_attribute_arguments (parser, takes_identifier, > - require_string, false); > + require_string, assume_attr, false); > } > else > c_parser_balanced_token_sequence (parser); > @@ -6248,8 +6326,21 @@ c_parser_statement_after_labels (c_parse > break; > case RID_ATTRIBUTE: > { > - /* Allow '__attribute__((fallthrough));'. */ > + /* Allow '__attribute__((fallthrough));' or > + '__attribute__((assume(cond)));'. */ > tree attrs = c_parser_gnu_attributes (parser); > + bool has_assume = lookup_attribute ("assume", attrs); > + if (has_assume) > + { > + if (c_parser_next_token_is (parser, CPP_SEMICOLON)) > + attrs = handle_assume_attribute (loc, attrs, true); > + else > + { > + warning_at (loc, OPT_Wattributes, > + "% attribute not followed by %<;%>"); > + has_assume = false; > + } > + } > if (attribute_fallthrough_p (attrs)) > { > if (c_parser_next_token_is (parser, CPP_SEMICOLON)) > @@ -6266,9 +6357,13 @@ c_parser_statement_after_labels (c_parse > "% attribute not followed " > "by %<;%>"); > } > + else if (has_assume) > + /* Eat the ';'. */ > + c_parser_consume_token (parser); > else if (attrs != NULL_TREE) > - warning_at (loc, OPT_Wattributes, "only attribute %" > - " can be applied to a null statement"); > + warning_at (loc, OPT_Wattributes, > + "only attribute % or % can " > + "be applied to a null statement"); > break; > } > default: > --- gcc/cp/cp-tree.h.jj 2022-09-22 00:14:18.970095904 +0200 > +++ gcc/cp/cp-tree.h 2022-09-22 00:24:01.094178623 +0200 > @@ -8235,6 +8235,7 @@ extern tree predeclare_vla (tree); > extern void clear_fold_cache (void); > extern tree lookup_hotness_attribute (tree); > extern tree process_stmt_hotness_attribute (tree, location_t); > +extern tree process_stmt_assume_attribute (tree, tree, location_t); > extern bool simple_empty_class_p (tree, tree, tree_code); > extern tree fold_builtin_source_location (location_t); > > --- gcc/cp/name-lookup.h.jj 2022-09-22 00:14:19.088094299 +0200 > +++ gcc/cp/name-lookup.h 2022-09-22 00:24:01.094178623 +0200 > @@ -212,7 +212,8 @@ enum scope_kind { > explicit specialization is introduced by > "template <>", this scope is always empty. */ > sk_transaction, /* A synchronized or atomic statement. */ > - sk_omp /* An OpenMP structured block. */ > + sk_omp, /* An OpenMP structured block. */ > + sk_assume /* The scope of assume attribute. */ > }; > > struct GTY(()) cp_class_binding { > --- gcc/cp/parser.cc.jj 2022-09-22 00:14:19.126093782 +0200 > +++ gcc/cp/parser.cc 2022-09-22 11:10:57.813376547 +0200 > @@ -2251,7 +2251,7 @@ static vec *cp_parser_paren > (cp_parser *, int, bool, bool, bool *, location_t * = NULL, > bool = false); > /* Values for the second parameter of cp_parser_parenthesized_expression_list. */ > -enum { non_attr = 0, normal_attr = 1, id_attr = 2 }; > +enum { non_attr = 0, normal_attr = 1, id_attr = 2, assume_attr = 3 }; > static void cp_parser_pseudo_destructor_name > (cp_parser *, tree, tree *, tree *); > static cp_expr cp_parser_unary_expression > @@ -8542,10 +8542,27 @@ cp_parser_parenthesized_expression_list > } > else > { > - expr > - = cp_parser_parenthesized_expression_list_elt (parser, cast_p, > - allow_expansion_p, > - non_constant_p); > + if (is_attribute_list == assume_attr) > + { > + enum scope_kind prev_kind = sk_block; > + if (current_binding_level) > + { > + prev_kind = current_binding_level->kind; > + current_binding_level->kind = sk_assume; > + } > + /* Parse conditional-expression. */ Let's factor this out of here and cp_parser_constant_expression rather than duplicate it. > + expr = cp_parser_binary_expression (parser, false, false, > + PREC_NOT_OPERATOR, NULL); > + /* If the next token is a `?' then we're actually looking at > + a conditional-expression; otherwise we're done. */ > + if (cp_lexer_next_token_is (parser->lexer, CPP_QUERY)) > + expr = cp_parser_question_colon_clause (parser, expr); > + if (current_binding_level) > + current_binding_level->kind = prev_kind; > + } > + else > + expr = cp_parser_parenthesized_expression_list_elt > + (parser, cast_p, allow_expansion_p, non_constant_p); > > if (wrap_locations_p) > expr.maybe_add_location_wrapper (); > @@ -12625,6 +12642,9 @@ cp_parser_statement (cp_parser* parser, > /* Look for an expression-statement instead. */ > statement = cp_parser_expression_statement (parser, in_statement_expr); > > + std_attrs = process_stmt_assume_attribute (std_attrs, statement, > + attrs_loc); > + > /* Handle [[fallthrough]];. */ > if (attribute_fallthrough_p (std_attrs)) > { > @@ -12648,7 +12668,7 @@ cp_parser_statement (cp_parser* parser, > if (statement && STATEMENT_CODE_P (TREE_CODE (statement))) > SET_EXPR_LOCATION (statement, statement_location); > > - /* Allow "[[fallthrough]];", but warn otherwise. */ > + /* Allow "[[fallthrough]];" or "[[assume(cond)]];", but warn otherwise. */ > if (std_attrs != NULL_TREE) > warning_at (attrs_loc, > OPT_Wattributes, > @@ -12840,6 +12860,8 @@ cp_parser_expression_statement (cp_parse > } > } > > + attr = process_stmt_assume_attribute (attr, statement, loc); > + > /* Handle [[fallthrough]];. */ > if (attribute_fallthrough_p (attr)) > { > @@ -28971,6 +28993,8 @@ cp_parser_gnu_attribute_list (cp_parser* > vec *vec; > int attr_flag = (attribute_takes_identifier_p (identifier) > ? id_attr : normal_attr); > + if (is_attribute_p ("assume", identifier)) > + attr_flag = assume_attr; > vec = cp_parser_parenthesized_expression_list > (parser, attr_flag, /*cast_p=*/false, > /*allow_expansion_p=*/false, > @@ -29222,6 +29246,9 @@ cp_parser_std_attribute (cp_parser *pars > /* C++17 fallthrough attribute is equivalent to GNU's. */ > else if (is_attribute_p ("fallthrough", attr_id)) > TREE_PURPOSE (TREE_PURPOSE (attribute)) = gnu_identifier; > + /* C++23 assume attribute is equivalent to GNU's. */ > + else if (is_attribute_p ("assume", attr_id)) > + TREE_PURPOSE (TREE_PURPOSE (attribute)) = gnu_identifier; > /* Transactional Memory TS optimize_for_synchronized attribute is > equivalent to GNU transaction_callable. */ > else if (is_attribute_p ("optimize_for_synchronized", attr_id)) > @@ -29266,8 +29293,12 @@ cp_parser_std_attribute (cp_parser *pars > return error_mark_node; > } > > - if (attr_ns == gnu_identifier > - && attribute_takes_identifier_p (attr_id)) > + if (is_attribute_p ("assume", attr_id) > + && (attr_ns == NULL_TREE || attr_ns == gnu_identifier)) > + /* The assume attribute needs special handling of the argument. */ > + attr_flag = assume_attr; > + else if (attr_ns == gnu_identifier > + && attribute_takes_identifier_p (attr_id)) > /* A GNU attribute that takes an identifier in parameter. */ > attr_flag = id_attr; > > --- gcc/cp/cp-gimplify.cc.jj 2022-09-22 00:14:18.929096461 +0200 > +++ gcc/cp/cp-gimplify.cc 2022-09-22 11:01:38.217041306 +0200 > @@ -3064,6 +3064,49 @@ process_stmt_hotness_attribute (tree std > return std_attrs; > } > > +/* If [[assume (cond)]] appears on this statement, handle it. */ > + > +tree > +process_stmt_assume_attribute (tree std_attrs, tree statement, > + location_t attrs_loc) > +{ > + if (std_attrs == error_mark_node) > + return std_attrs; > + tree attr = lookup_attribute ("gnu", "assume", std_attrs); > + if (!attr) > + return std_attrs; > + /* The next token after the assume attribute is not ';'. */ > + if (statement) > + { > + warning_at (attrs_loc, OPT_Wattributes, > + "% attribute not followed by %<;%>"); > + attr = NULL_TREE; > + } > + for (; attr; attr = lookup_attribute ("gnu", "assume", TREE_CHAIN (attr))) > + { > + tree args = TREE_VALUE (attr); > + int nargs = list_length (args); > + if (nargs != 1) > + { Need auto_diagnostic_group. > + error_at (attrs_loc, "wrong number of arguments specified for " > + "%qE attribute", get_attribute_name (attr)); > + inform (attrs_loc, "expected %i, found %i", 1, nargs); > + } > + else > + { > + tree arg = TREE_VALUE (args); > + if (!type_dependent_expression_p (arg)) > + arg = contextual_conv_bool (arg, tf_warning_or_error); > + if (error_operand_p (arg)) > + continue; > + statement = build_call_expr_internal_loc (attrs_loc, IFN_ASSUME, > + void_type_node, 1, arg); > + finish_expr_stmt (statement); > + } > + } > + return remove_attribute ("gnu", "assume", std_attrs); > +} > + > /* Helper of fold_builtin_source_location, return the > std::source_location::__impl type after performing verification > on it. LOC is used for reporting any errors. */ > --- gcc/cp/decl.cc.jj 2022-09-22 00:14:55.478599363 +0200 > +++ gcc/cp/decl.cc 2022-09-22 00:24:01.121178256 +0200 > @@ -223,6 +223,7 @@ struct GTY((for_user)) named_label_entry > bool in_transaction_scope; > bool in_constexpr_if; > bool in_consteval_if; > + bool in_assume; I think it would be better to reject jumps into statement-expressions like the C front-end. > }; > > #define named_labels cp_function_chain->x_named_labels > @@ -543,6 +544,8 @@ poplevel_named_label_1 (named_label_entr > ent->in_constexpr_if = true; > else if (level_for_consteval_if (bl->level_chain)) > ent->in_consteval_if = true; > + else if (bl->level_chain->kind == sk_assume) > + ent->in_assume = true; > break; > default: > break; > @@ -3487,7 +3490,7 @@ check_previous_goto_1 (tree decl, cp_bin > bool complained = false; > int identified = 0; > bool saw_eh = false, saw_omp = false, saw_tm = false, saw_cxif = false; > - bool saw_ceif = false; > + bool saw_ceif = false, saw_assume = false; > > if (exited_omp) > { > @@ -3573,6 +3576,11 @@ check_previous_goto_1 (tree decl, cp_bin > loc = EXPR_LOCATION (b->level_chain->this_entity); > saw_ceif = true; > } > + else if (!saw_assume && b->level_chain->kind == sk_assume) > + { > + inf = G_(" enters % attribute condition"); > + saw_assume = true; > + } > break; > > default: > @@ -3650,12 +3658,13 @@ check_goto (tree decl) > > if (ent->in_try_scope || ent->in_catch_scope || ent->in_transaction_scope > || ent->in_constexpr_if || ent->in_consteval_if > - || ent->in_omp_scope || !vec_safe_is_empty (ent->bad_decls)) > + || ent->in_omp_scope || ent->in_assume > + || !vec_safe_is_empty (ent->bad_decls)) > { > diagnostic_t diag_kind = DK_PERMERROR; > if (ent->in_try_scope || ent->in_catch_scope || ent->in_constexpr_if > || ent->in_consteval_if || ent->in_transaction_scope > - || ent->in_omp_scope) > + || ent->in_omp_scope || ent->in_assume) > diag_kind = DK_ERROR; > complained = identify_goto (decl, DECL_SOURCE_LOCATION (decl), > &input_location, diag_kind); > @@ -3703,6 +3712,8 @@ check_goto (tree decl) > inform (input_location, " enters % statement"); > else if (ent->in_consteval_if) > inform (input_location, " enters % statement"); > + else if (ent->in_assume) > + inform (input_location, " enters % attribute condition"); > } > > if (ent->in_omp_scope) > --- gcc/cp/constexpr.cc.jj 2022-09-22 00:14:18.879097141 +0200 > +++ gcc/cp/constexpr.cc 2022-09-22 00:24:01.123178229 +0200 > @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3. > #include "opts.h" > #include "stringpool.h" > #include "attribs.h" > +#include "fold-const.h" > > static bool verify_constant (tree, bool, bool *, bool *); > #define VERIFY_CONSTANT(X) \ > @@ -1837,6 +1838,33 @@ cxx_eval_internal_function (const conste > case IFN_FALLTHROUGH: > return void_node; > > + case IFN_ASSUME: > + /* For now, restrict constexpr evaluation of [[assume (cond)]] > + only to the cases which don't have side-effects. Evaluating > + it even when it does would mean we'd need to somehow undo > + all the side-effects e.g. in ctx->global->values. */ > + if (simple_operand_p_2 (CALL_EXPR_ARG (t, 0)) > + /* And it needs to be a potential constant expression. */ > + && potential_rvalue_constant_expression (CALL_EXPR_ARG (t, 0))) > + { > + constexpr_ctx new_ctx = *ctx; > + new_ctx.quiet = true; > + tree arg = CALL_EXPR_ARG (t, 0); > + bool new_non_constant_p = false, new_overflow_p = false; > + arg = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue, > + &new_non_constant_p, > + &new_overflow_p); > + if (!new_non_constant_p && !new_overflow_p && integer_zerop (arg)) > + { > + if (!*non_constant_p && !ctx->quiet) > + error_at (EXPR_LOCATION (t), > + "failed % attribute assumption"); Maybe share some code for explaining the failure with finish_static_assert? > + *non_constant_p = true; > + return t; > + } > + } > + return void_node; > + > case IFN_ADD_OVERFLOW: > opcode = PLUS_EXPR; > break; > @@ -8706,6 +8734,7 @@ potential_constant_expression_1 (tree t, > case IFN_UBSAN_BOUNDS: > case IFN_UBSAN_VPTR: > case IFN_FALLTHROUGH: > + case IFN_ASSUME: > return true; > > case IFN_ADD_OVERFLOW: > --- gcc/cp/pt.cc.jj 2022-09-22 00:14:19.185092980 +0200 > +++ gcc/cp/pt.cc 2022-09-22 00:24:01.150177861 +0200 > @@ -21152,6 +21152,34 @@ tsubst_copy_and_build (tree t, > break; > } > > + case IFN_ASSUME: > + gcc_assert (nargs == 1); > + if (vec_safe_length (call_args) != 1) > + { > + error_at (cp_expr_loc_or_input_loc (t), > + "wrong number of arguments to " > + "% attribute"); > + ret = error_mark_node; > + } > + else > + { > + if (!type_dependent_expression_p ((*call_args)[0])) > + (*call_args)[0] > + = contextual_conv_bool ((*call_args)[0], > + tf_warning_or_error); > + if (error_operand_p ((*call_args)[0])) > + { > + ret = error_mark_node; > + break; > + } > + ret = build_call_expr_internal_loc (EXPR_LOCATION (t), > + IFN_ASSUME, > + void_type_node, 1, > + (*call_args)[0]); > + RETURN (ret); > + } > + break; > + > default: > /* Unsupported internal function with arguments. */ > gcc_unreachable (); > --- gcc/testsuite/gcc.dg/attr-assume-1.c.jj 2022-09-22 00:24:01.151177848 +0200 > +++ gcc/testsuite/gcc.dg/attr-assume-1.c 2022-09-22 00:24:01.151177848 +0200 > @@ -0,0 +1,69 @@ > +/* Portable assumptions */ > +/* { dg-do run } */ > +/* { dg-options "-std=c2x" } */ > + > +int > +f1 (int i) > +{ > + [[gnu::assume (i == 42)]]; > + return i; > +} > + > +int > +f2 (int i) > +{ > + __attribute__ ((assume (++i == 44))); > + return i; > +} > + > +int a; > +int *volatile c; > + > +int > +f3 () > +{ > + ++a; > + return 1; > +} > + > +int > +f4 (double x) > +{ > + [[gnu::assume (__builtin_isfinite (x) && x >= 0.0)]]; > + return __builtin_isfinite (__builtin_sqrt (x)); > +} > + > +double > +f5 (double x) > +{ > + __attribute__((assume (__builtin_isfinite (__builtin_sqrt (x))))); > + return __builtin_sqrt (x); > +} > + > +int > +f6 (int x) > +{ > + [[gnu::assume (x == 93 ? 1 : 0)]]; > + return x; > +} > + > +int > +main () > +{ > + int b = 42; > + double d = 42.0, e = 43.0; > + c = &b; > + [[__gnu__::__assume__ (f3 ())]]; > + if (a) > + __builtin_abort (); > + [[gnu::assume (++b == 43)]]; > + if (b != 42 || *c != 42) > + __builtin_abort (); > + __attribute__((assume (d < e))); > + int i = 90, j = 91, k = 92; > + [[gnu::__assume__ (i == 90), gnu::assume (j <= 91)]] [[gnu::assume (k >= 92)]] > + ; > + __attribute__((__assume__ (i == 90), assume (j <= 91))) __attribute__((assume (k >= 92))); > + if (f6 (93) != 93) > + __builtin_abort (); > +} > --- gcc/testsuite/gcc.dg/attr-assume-2.c.jj 2022-09-22 00:24:01.151177848 +0200 > +++ gcc/testsuite/gcc.dg/attr-assume-2.c 2022-09-22 00:24:01.151177848 +0200 > @@ -0,0 +1,66 @@ > +/* Portable assumptions */ > +/* { dg-do compile } */ > +/* { dg-options "-std=c2x" } */ > + > +[[gnu::__assume__ (1)]] void f1 (void); /* { dg-warning "'assume' attribute not followed by ';'" } */ > + /* { dg-warning "'assume' attribute ignored" "" { target *-*-* } .-1 } */ > +typedef int intx [[gnu::assume (1)]]; /* { dg-warning "'assume' attribute ignored" } */ > +[[__gnu__::assume (1)]]; /* { dg-warning "'assume' attribute at top level" } */ > +__attribute__((assume (1))) void f1b ();/* { dg-warning "'assume' attribute not followed by ';'" } */ > + /* { dg-warning "'assume' attribute ignored" "" { target *-*-* } .-1 } */ > +typedef int inty __attribute__((assume (1))); /* { dg-warning "'assume' attribute ignored" } */ > + > +void > +foo () > +{ > + int i; > + [[gnu::assume]]; /* { dg-error "wrong number of arguments specified for 'assume' attribute" } */ > + /* { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } */ > + [[gnu::__assume__ ()]]; /* { dg-error "parentheses must be omitted if attribute argument list is empty" } */ > + /* { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 } */ > + /* { dg-message "expected 1, found 0" "" { target *-*-* } .-2 } */ > + [[gnu::assume (1, 1)]]; /* { dg-error "wrong number of arguments specified for 'assume' attribute" } */ > + /* { dg-message "expected 1, found 2" "" { target *-*-* } .-1 } */ > + [[gnu::assume (1)]] i = 1; /* { dg-warning "'assume' attribute ignored" } */ > + [[gnu::assume (i = 1)]]; /* { dg-error "expected" } */ > + /* { dg-warning "'assume' attribute ignored" "" { target *-*-* } .-1 } */ > + __attribute__((assume)); /* { dg-error "wrong number of arguments specified for 'assume' attribute" } */ > + /* { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } */ > + __attribute__((assume ())); /* { dg-error "wrong number of arguments specified for 'assume' attribute" } */ > + /* { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } */ > + __attribute__((assume (1, 1))); /* { dg-error "wrong number of arguments specified for 'assume' attribute" } */ > + /* { dg-message "expected 1, found 2" "" { target *-*-* } .-1 } */ > + __attribute__((assume (i = 1))); /* { dg-error "expected" } */ > +} > + > +int > +f2 (int x) > +{ > + __asm ("" : "+r" (x)); > + return x; > +} > + > +int > +f3 (int x) > +{ > + [[gnu::assume (f2 (42) == 42)]]; > + return x; > +} > + > +int > +f3a (int x) > +{ > + __attribute__((assume (f2 (42) == 42))); > + return x; > +} > + > +struct S {}; > + > +int > +f4 () > +{ > + struct S s; > + [[gnu::assume (s)]]; /* { dg-error "used struct type value where scalar is required" } */ > + __attribute__((assume (s))); /* { dg-error "used struct type value where scalar is required" } */ > + return 0; > +} > --- gcc/testsuite/gcc.dg/attr-assume-3.c.jj 2022-09-22 00:24:01.151177848 +0200 > +++ gcc/testsuite/gcc.dg/attr-assume-3.c 2022-09-22 00:24:01.151177848 +0200 > @@ -0,0 +1,35 @@ > +/* Portable assumptions */ > +/* { dg-do compile } */ > +/* { dg-options "-std=c2x" } */ > + > +void > +foo (int x) > +{ > + if (x == 1) > + goto l1; /* { dg-error "jump into statement expression" } */ > + else if (x == 2) > + goto l2; /* { dg-error "jump into statement expression" } */ > + else if (x == 3) > + goto l3; /* { dg-error "jump into statement expression" } */ > + [[gnu::assume (({ l0:; if (x == 0) goto l0; 1; }))]]; > + [[gnu::assume (({ if (x == 0) __builtin_abort (); 1; }))]]; > + [[gnu::assume (({ l1:; 1; }))]]; /* { dg-message "label 'l1' defined here" } */ > + [[gnu::assume (({ l2:; 1; }))]]; /* { dg-message "label 'l2' defined here" } */ > + __attribute__((assume (({ l3:; 1; })))); /* { dg-message "label 'l3' defined here" } */ > + [[gnu::assume (({ l4:; 1; }))]]; /* { dg-message "label 'l4' defined here" } */ > + [[gnu::assume (({ l5:; 1; }))]]; /* { dg-message "label 'l5' defined here" } */ > + __attribute__((assume (({ l6:; 1; })))); /* { dg-message "label 'l6' defined here" } */ > + switch (x) /* { dg-message "switch starts here" } */ > + { > + case 7: > + [[gnu::assume (({ case 8:; 1; }))]]; /* { dg-error "switch jumps into statement expression" } */ > + __attribute__((assume (({ default:; 1; })))); /* { dg-error "switch jumps into statement expression" } */ > + break; > + } > + if (x == 4) > + goto l4; /* { dg-error "jump into statement expression" } */ > + else if (x == 5) > + goto l5; /* { dg-error "jump into statement expression" } */ > + else if (x == 6) > + goto l6; /* { dg-error "jump into statement expression" } */ > +} > --- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C.jj 2022-09-22 00:14:19.718085731 +0200 > +++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C 2022-09-22 00:24:01.176177508 +0200 > @@ -422,7 +422,7 @@ > # error "__cpp_nontype_template_parameter_auto != 201606" > #endif > > -// C++20 features > +// C++20 features: > > #ifndef __cpp_conditional_explicit > # error "__cpp_conditional_explicit" > @@ -460,6 +460,44 @@ > # error "__cpp_aggregate_paren_init != 201902" > #endif > > +#ifndef __cpp_char8_t > +# error "__cpp_char8_t" > +#elif __cpp_char8_t != 201811 > +# error "__cpp_char8_t != 201811" > +#endif > + > +#ifndef __cpp_designated_initializers > +# error "__cpp_designated_initializers" > +#elif __cpp_designated_initializers != 201707 > +# error "__cpp_designated_initializers != 201707" > +#endif > + > +#ifndef __cpp_constexpr_in_decltype > +# error "__cpp_constexpr_in_decltype" > +#elif __cpp_constexpr_in_decltype != 201711 > +# error "__cpp_constexpr_in_decltype != 201711" > +#endif > + > +#ifndef __cpp_consteval > +# error "__cpp_consteval" > +#elif __cpp_consteval != 201811 > +# error "__cpp_consteval != 201811" > +#endif > + > +#ifndef __cpp_concepts > +# error "__cpp_concepts" > +#elif __cpp_concepts != 202002 > +# error "__cpp_concepts != 202002" > +#endif > + > +#ifndef __cpp_using_enum > +# error "__cpp_using_enum" > +#elif __cpp_using_enum != 201907 > +# error "__cpp_using_enum != 201907" > +#endif > + > +// C++20 attributes: > + > #ifdef __has_cpp_attribute > > # if ! __has_cpp_attribute(maybe_unused) > @@ -501,39 +539,3 @@ > #else > # error "__has_cpp_attribute" > #endif > - > -#ifndef __cpp_char8_t > -# error "__cpp_char8_t" > -#elif __cpp_char8_t != 201811 > -# error "__cpp_char8_t != 201811" > -#endif > - > -#ifndef __cpp_designated_initializers > -# error "__cpp_designated_initializers" > -#elif __cpp_designated_initializers != 201707 > -# error "__cpp_designated_initializers != 201707" > -#endif > - > -#ifndef __cpp_constexpr_in_decltype > -# error "__cpp_constexpr_in_decltype" > -#elif __cpp_constexpr_in_decltype != 201711 > -# error "__cpp_constexpr_in_decltype != 201711" > -#endif > - > -#ifndef __cpp_consteval > -# error "__cpp_consteval" > -#elif __cpp_consteval != 201811 > -# error "__cpp_consteval != 201811" > -#endif > - > -#ifndef __cpp_concepts > -# error "__cpp_concepts" > -#elif __cpp_concepts != 202002 > -# error "__cpp_concepts != 202002" > -#endif > - > -#ifndef __cpp_using_enum > -# error "__cpp_using_enum" > -#elif __cpp_using_enum != 201907 > -# error "__cpp_using_enum != 201907" > -#endif > --- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj 2022-09-22 00:14:19.703085935 +0200 > +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C 2022-09-22 00:24:01.196177236 +0200 > @@ -422,7 +422,7 @@ > # error "__cpp_nontype_template_parameter_auto != 201606" > #endif > > -// C++20 features > +// C++20 features: > > #ifndef __cpp_conditional_explicit > # error "__cpp_conditional_explicit" > @@ -460,6 +460,44 @@ > # error "__cpp_aggregate_paren_init != 201902" > #endif > > +#ifndef __cpp_char8_t > +# error "__cpp_char8_t" > +#elif __cpp_char8_t != 201811 > +# error "__cpp_char8_t != 201811" > +#endif > + > +#ifndef __cpp_designated_initializers > +# error "__cpp_designated_initializers" > +#elif __cpp_designated_initializers != 201707 > +# error "__cpp_designated_initializers != 201707" > +#endif > + > +#ifndef __cpp_constexpr_in_decltype > +# error "__cpp_constexpr_in_decltype" > +#elif __cpp_constexpr_in_decltype != 201711 > +# error "__cpp_constexpr_in_decltype != 201711" > +#endif > + > +#ifndef __cpp_consteval > +# error "__cpp_consteval" > +#elif __cpp_consteval != 201811 > +# error "__cpp_consteval != 201811" > +#endif > + > +#ifndef __cpp_concepts > +# error "__cpp_concepts" > +#elif __cpp_concepts != 202002 > +# error "__cpp_concepts != 202002" > +#endif > + > +#ifndef __cpp_using_enum > +# error "__cpp_using_enum" > +#elif __cpp_using_enum != 201907 > +# error "__cpp_using_enum != 201907" > +#endif > + > +// C++20 attributes: > + > #ifdef __has_cpp_attribute > > # if ! __has_cpp_attribute(maybe_unused) > @@ -502,42 +540,6 @@ > # error "__has_cpp_attribute" > #endif > > -#ifndef __cpp_char8_t > -# error "__cpp_char8_t" > -#elif __cpp_char8_t != 201811 > -# error "__cpp_char8_t != 201811" > -#endif > - > -#ifndef __cpp_designated_initializers > -# error "__cpp_designated_initializers" > -#elif __cpp_designated_initializers != 201707 > -# error "__cpp_designated_initializers != 201707" > -#endif > - > -#ifndef __cpp_constexpr_in_decltype > -# error "__cpp_constexpr_in_decltype" > -#elif __cpp_constexpr_in_decltype != 201711 > -# error "__cpp_constexpr_in_decltype != 201711" > -#endif > - > -#ifndef __cpp_consteval > -# error "__cpp_consteval" > -#elif __cpp_consteval != 201811 > -# error "__cpp_consteval != 201811" > -#endif > - > -#ifndef __cpp_concepts > -# error "__cpp_concepts" > -#elif __cpp_concepts != 202002 > -# error "__cpp_concepts != 202002" > -#endif > - > -#ifndef __cpp_using_enum > -# error "__cpp_using_enum" > -#elif __cpp_using_enum != 201907 > -# error "__cpp_using_enum != 201907" > -#endif > - > // C++23 features: > > #ifndef __cpp_size_t_suffix > @@ -563,3 +565,15 @@ > #elif __cpp_named_character_escapes != 202207 > # error "__cpp_named_character_escapes != 202207" > #endif > + > +// C++23 attributes: > + > +#ifdef __has_cpp_attribute > +# if ! __has_cpp_attribute(assume) > +# error "__has_cpp_attribute(assume)" > +# elif __has_cpp_attribute(assume) != 202207 > +# error "__has_cpp_attribute(assume) != 202207" > +# endif > +#else > +# error "__has_cpp_attribute" > +#endif > --- gcc/testsuite/g++.dg/cpp23/attr-assume1.C.jj 2022-09-22 00:24:01.196177236 +0200 > +++ gcc/testsuite/g++.dg/cpp23/attr-assume1.C 2022-09-22 00:24:01.196177236 +0200 > @@ -0,0 +1,191 @@ > +// P1774R8 - Portable assumptions > +// { dg-do run { target c++11 } } > + > +namespace std > +{ > + constexpr bool > + isfinite (float x) > + { return __builtin_isfinite (x); } > + > + constexpr bool > + isfinite (double x) > + { return __builtin_isfinite (x); } > + > + constexpr bool > + isfinite (long double x) > + { return __builtin_isfinite (x); } > + > + constexpr float > + sqrt (float x) > + { return __builtin_sqrtf (x); } > + > + constexpr double > + sqrt (double x) > + { return __builtin_sqrt (x); } > + > + constexpr long double > + sqrt (long double x) > + { return __builtin_sqrtl (x); } > + > + extern "C" void > + abort (); > +} > + > +constexpr int > +f1 (int i) > +{ > +#if __cpp_constexpr >= 201603L > + auto f = [=] { [[assume (i == 0)]]; }; > + return sizeof (f); > +#else > + return sizeof (int); > +#endif > +} > + > +void > +f2 () > +{ > + static_assert (f1 (0) >= sizeof (int), ""); > +} > + > +int > +f3 (int i) > +{ > + [[assume (i == 42)]]; > + return i; > +} > + > +int > +f4 (int i) > +{ > + [[assume (++i == 44)]]; > + return i; > +} > + > +int a; > +int *volatile c; > + > +bool > +f5 () > +{ > + ++a; > + return true; > +} > + > +constexpr int > +f6 () > +{ > +#if __cpp_constexpr >= 201304L > + [[assume (f5 ())]]; > +#endif > + return 1; > +} > + > +template > +bool > +f7 () > +{ > +#if __cpp_fold_expressions >= 201411L > + [[assume (((args >= 0) && ...))]]; > + return ((args >= 0) && ...); > +#else > + return true; > +#endif > +} > + > +bool > +f8 (double x) > +{ > + [[assume (std::isfinite (x) && x >= 0.0)]]; > + return std::isfinite (std::sqrt (x)); > +} > + > +double > +f9 (double x) > +{ > + [[assume (std::isfinite (std::sqrt (x)))]]; > + return std::sqrt (x); > +} > + > +template > +T > +f10 (T x) > +{ > + [[assume (x == N)]]; > + return x; > +} > + > +int > +f11 (int x) > +{ > + [[assume (x == 93 ? true : throw 1)]]; > + return x; > +} > + > +constexpr int > +f12 (int x) > +{ > +#if __cpp_constexpr >= 201304L > + [[assume (++x == 43)]]; > +#endif > + return x; > +} > + > +static_assert (f12 (42) == 42, ""); > + > +struct S > +{ > + operator bool () { return true; } > +}; > + > +int > +f13 () > +{ > + S s; > + [[assume (s)]]; > + return 0; > +} > + > +template > +int > +f14 () > +{ > + T t; > + [[assume (t)]]; > + return 0; > +} > + > +int > +main () > +{ > + int b = 42; > + double d = 42.0, e = 43.0; > + c = &b; > + [[assume (f5 ())]]; > + if (a) > + std::abort (); > + [[assume (++b == 43)]]; > + if (b != 42 || *c != 42) > + std::abort (); > + static_assert (f6 () == 1, ""); > + if (f6 () != 1) > + std::abort (); > + if (a) > + std::abort (); > + if (!f7 <0> () || !f7 <1, 2, 3, 4> ()) > + std::abort (); > + [[assume (d < e)]]; > + if (f10 (45) != 45 > + || f10 (128LL) != 128LL > +#if __cpp_nontype_template_args >= 201911L > + || f10 (-42.0L) != -42.0L > +#endif > + || false) > + std::abort (); > + int i = 90, j = 91, k = 92; > + [[assume (i == 90), assume (j <= 91)]] [[assume (k >= 92)]]; > + if (f11 (93) != 93) > + std::abort (); > + if (f14 () != 0) > + std::abort (); > +} > --- gcc/testsuite/g++.dg/cpp23/attr-assume2.C.jj 2022-09-22 00:24:01.214176991 +0200 > +++ gcc/testsuite/g++.dg/cpp23/attr-assume2.C 2022-09-22 00:24:01.196177236 +0200 > @@ -0,0 +1,72 @@ > +// P1774R8 - Portable assumptions > +// { dg-do compile { target c++11 } } > + > +[[assume (true)]] void f1 (); // { dg-error "'assume' attribute ignored" } > +typedef int intx [[assume (true)]]; // { dg-error "'assume' attribute ignored" } > +[[assume (true)]]; // { dg-warning "attribute ignored" } > + > +void > +foo () > +{ > + int i; > + [[assume]]; // { dg-error "wrong number of arguments specified for 'assume' attribute" } > + // { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } > + [[assume ()]]; // { dg-error "parentheses must be omitted if 'assume' attribute argument list is empty" } > + // { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 } > + // { dg-message "expected 1, found 0" "" { target *-*-* } .-2 } > + [[assume (true, true)]]; // { dg-error "wrong number of arguments specified for 'assume' attribute" } > + // { dg-message "expected 1, found 2" "" { target *-*-* } .-1 } > + [[assume (true)]] i = 1; // { dg-warning "'assume' attribute not followed by ';'" } > + [[assume (throw 1)]]; // { dg-error "expected primary-expression before 'throw'" } > + [[assume (i = 1)]]; // { dg-error "expected '\\\)' before '=' token" } > +} > + > +constexpr int > +f2 (int x) > +{ > +#if __cpp_constexpr >= 201304L > + [[assume (x == 42)]]; // { dg-error "failed 'assume' attribute assumption" "" { target c++14 } } > +#endif > + return x; > +} > + > +constexpr int a = f2 (44); > + > +int > +f3 (int x) > +{ > + __asm ("" : "+r" (x)); > + return x; > +} > + > +constexpr int > +f4 (int x) > +{ > +#if __cpp_constexpr >= 201304L > + [[assume (f3 (42) == 42)]]; > +#endif > + return x; > +} > + > +static_assert (f4 (42) == 42, ""); > + > +struct S {}; > + > +int > +f5 () > +{ > + S s; > + [[assume (s)]]; // { dg-error "could not convert 's' from 'S' to 'bool'" } > + return 0; > +} > + > +template > +int > +f6 () > +{ > + T t; > + [[assume (t)]]; // { dg-error "could not convert 't' from 'S' to 'bool'" } > + return 0; > +} > + > +int z = f6 (); > --- gcc/testsuite/g++.dg/cpp23/attr-assume3.C.jj 2022-09-22 00:24:01.214176991 +0200 > +++ gcc/testsuite/g++.dg/cpp23/attr-assume3.C 2022-09-22 00:24:01.214176991 +0200 > @@ -0,0 +1,198 @@ > +// P1774R8 - Portable assumptions > +// { dg-do run { target c++11 } } > + > +namespace std > +{ > + constexpr bool > + isfinite (float x) > + { return __builtin_isfinite (x); } > + > + constexpr bool > + isfinite (double x) > + { return __builtin_isfinite (x); } > + > + constexpr bool > + isfinite (long double x) > + { return __builtin_isfinite (x); } > + > + constexpr float > + sqrt (float x) > + { return __builtin_sqrtf (x); } > + > + constexpr double > + sqrt (double x) > + { return __builtin_sqrt (x); } > + > + constexpr long double > + sqrt (long double x) > + { return __builtin_sqrtl (x); } > + > + extern "C" void > + abort (); > +} > + > +constexpr int > +f1 (int i) > +{ > +#if __cpp_constexpr >= 201603L > + auto f = [=] { [[__assume__ (i == 0)]]; }; > + return sizeof (f); > +#else > + return sizeof (int); > +#endif > +} > + > +void > +f2 () > +{ > + static_assert (f1 (0) >= sizeof (int), ""); > +} > + > +int > +f3 (int i) > +{ > + [[gnu::assume (i == 42)]]; > + return i; > +} > + > +int > +f4 (int i) > +{ > + __attribute__ ((assume (++i == 44))); > + return i; > +} > + > +int a; > +int *volatile c; > + > +bool > +f5 () > +{ > + ++a; > + return true; > +} > + > +constexpr int > +f6 () > +{ > +#if __cpp_constexpr >= 201304L > + [[__assume__ (f5 ())]]; > +#endif > + return 1; > +} > + > +template > +bool > +f7 () > +{ > +#if __cpp_fold_expressions >= 201411L > + [[__gnu__::__assume__ (((args >= 0) && ...))]]; > + return ((args >= 0) && ...); > +#else > + return true; > +#endif > +} > + > +bool > +f8 (double x) > +{ > + [[gnu::assume (std::isfinite (x) && x >= 0.0)]]; > + return std::isfinite (std::sqrt (x)); > +} > + > +double > +f9 (double x) > +{ > + __attribute__((assume (std::isfinite (std::sqrt (x))))); > + return std::sqrt (x); > +} > + > +template > +T > +f10 (T x) > +{ > + [[__assume__ (x == N)]]; > + return x; > +} > + > +int > +f11 (int x) > +{ > + [[gnu::assume (x == 93 ? true : throw 1)]]; > + return x; > +} > + > +constexpr int > +f12 (int x) > +{ > +#if __cpp_constexpr >= 201304L > + __attribute__((assume (++x == 43))); > +#endif > + return x; > +} > + > +static_assert (f12 (42) == 42, ""); > + > +struct S > +{ > + operator bool () { return true; } > +}; > + > +int > +f13 () > +{ > + S s; > + [[__gnu__::__assume__ (s)]]; > + return 0; > +} > + > +template > +int > +f14 () > +{ > + T t; > + __attribute__((assume (t))); > + return 0; > +} > + > +int > +main () > +{ > + int b = 42; > + double d = 42.0, e = 43.0; > + c = &b; > + [[__assume__ (f5 ())]]; > + if (a) > + std::abort (); > + [[gnu::assume (++b == 43)]]; > + if (b != 42 || *c != 42) > + std::abort (); > + static_assert (f6 () == 1, ""); > + if (f6 () != 1) > + std::abort (); > + if (a) > + std::abort (); > + if (!f7 <0> () || !f7 <1, 2, 3, 4> ()) > + std::abort (); > + __attribute__((assume (d < e))); > + if (f10 (45) != 45 > + || f10 (128LL) != 128LL > +#if __cpp_nontype_template_args >= 201911L > + || f10 (-42.0L) != -42.0L > +#endif > + || false) > + std::abort (); > + int i = 90, j = 91, k = 92; > + [[__assume__ (i == 90), gnu::assume (j <= 91)]] > +#if __cplusplus >= 201703L > + [[using gnu:assume (k >= 92)]] > +#else > + [[gnu::assume (k >= 92)]] > +#endif > + ; > + __attribute__((__assume__ (i == 90), assume (j <= 91))) __attribute__((assume (k >= 92))); > + if (f11 (93) != 93) > + std::abort (); > + if (f14 () != 0) > + std::abort (); > +} > --- gcc/testsuite/g++.dg/cpp23/attr-assume4.C.jj 2022-09-22 00:24:01.214176991 +0200 > +++ gcc/testsuite/g++.dg/cpp23/attr-assume4.C 2022-09-22 00:24:01.214176991 +0200 > @@ -0,0 +1,136 @@ > +// P1774R8 - Portable assumptions > +// { dg-do compile { target c++11 } } > + > +[[__assume__ (true)]] void f1 (); // { dg-error "'assume' attribute ignored" } > +typedef int intx [[__assume__ (true)]]; // { dg-error "'assume' attribute ignored" } > +[[__assume__ (true)]]; // { dg-warning "attribute ignored" } > +[[gnu::assume (true)]] void f1a (); // { dg-error "'assume' attribute ignored" } > +typedef int inty [[gnu::__assume__ (true)]]; // { dg-error "'assume' attribute ignored" } > +[[__gnu__::assume (true)]]; // { dg-warning "attribute ignored" } > +__attribute__((assume (true))) void f1b (); // { dg-error "'assume' attribute ignored" } > +typedef int intz __attribute__((assume (true)));// { dg-error "'assume' attribute ignored" } > + > +void > +foo () > +{ > + int i; > + [[__assume__]]; // { dg-error "wrong number of arguments specified for 'assume' attribute" } > + // { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } > + [[__assume__ ()]]; // { dg-error "parentheses must be omitted if 'assume' attribute argument list is empty" } > + // { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 } > + // { dg-message "expected 1, found 0" "" { target *-*-* } .-2 } > + [[__assume__ (true, true)]]; // { dg-error "wrong number of arguments specified for 'assume' attribute" } > + // { dg-message "expected 1, found 2" "" { target *-*-* } .-1 } > + [[__assume__ (true)]] i = 1; // { dg-warning "'assume' attribute not followed by ';'" } > + [[__assume__ (throw 1)]]; // { dg-error "expected primary-expression before 'throw'" } > + [[__assume__ (i = 1)]]; // { dg-error "expected '\\\)' before '=' token" } > + [[gnu::assume]]; // { dg-error "wrong number of arguments specified for 'assume' attribute" } > + // { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } > + [[gnu::assume ()]]; // { dg-error "parentheses must be omitted if 'assume' attribute argument list is empty" } > + // { dg-error "wrong number of arguments specified for 'assume' attribute" "" { target *-*-* } .-1 } > + // { dg-message "expected 1, found 0" "" { target *-*-* } .-2 } > + [[gnu::assume (true, true)]]; // { dg-error "wrong number of arguments specified for 'assume' attribute" } > + // { dg-message "expected 1, found 2" "" { target *-*-* } .-1 } > + [[gnu::assume (true)]] i = 1; // { dg-warning "'assume' attribute not followed by ';'" } > + [[gnu::assume (throw 1)]]; // { dg-error "expected primary-expression before 'throw'" } > + [[gnu::assume (i = 1)]]; // { dg-error "expected '\\\)' before '=' token" } > + __attribute__((assume)); // { dg-error "wrong number of arguments specified for 'assume' attribute" } > + // { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } > + __attribute__((assume ())); // { dg-error "wrong number of arguments specified for 'assume' attribute" } > + // { dg-message "expected 1, found 0" "" { target *-*-* } .-1 } > + __attribute__((assume (true, true))); // { dg-error "wrong number of arguments specified for 'assume' attribute" } > + // { dg-message "expected 1, found 2" "" { target *-*-* } .-1 } > + __attribute__((assume (true))) i = 1; // { dg-warning "'assume' attribute not followed by ';'" } > + __attribute__((assume (throw 1))); // { dg-error "expected primary-expression before 'throw'" } > + __attribute__((assume (i = 1))); // { dg-error "expected '\\\)' before '=' token" } > +} > + > +constexpr int > +f2 (int x) > +{ > +#if __cpp_constexpr >= 201304L > + [[__assume__ (x == 42)]]; // { dg-error "failed 'assume' attribute assumption" "" { target c++14 } } > +#endif > + return x; > +} > + > +constexpr int > +f2a (int x) > +{ > +#if __cpp_constexpr >= 201304L > + [[gnu::__assume__ (x == 42)]]; // { dg-error "failed 'assume' attribute assumption" "" { target c++14 } } > +#endif > + return x; > +} > + > +constexpr int > +f2b (int x) > +{ > +#if __cpp_constexpr >= 201304L > + __attribute__((__assume__ (x == 42)));// { dg-error "failed 'assume' attribute assumption" "" { target c++14 } } > +#endif > + return x; > +} > + > +constexpr int a = f2 (44); > +constexpr int aa = f2a (44); > +constexpr int ab = f2b (44); > + > +int > +f3 (int x) > +{ > + __asm ("" : "+r" (x)); > + return x; > +} > + > +constexpr int > +f4 (int x) > +{ > +#if __cpp_constexpr >= 201304L > + [[__assume__ (f3 (42) == 42)]]; > +#endif > + return x; > +} > + > +constexpr int > +f4a (int x) > +{ > +#if __cpp_constexpr >= 201304L > + [[gnu::assume (f3 (42) == 42)]]; > +#endif > + return x; > +} > + > +constexpr int > +f4b (int x) > +{ > +#if __cpp_constexpr >= 201304L > + __attribute__((assume (f3 (42) == 42))); > +#endif > + return x; > +} > + > +static_assert (f4 (42) == 42, ""); > +static_assert (f4a (42) == 42, ""); > +static_assert (f4b (42) == 42, ""); > + > +struct S {}; > + > +int > +f5 () > +{ > + S s; > + [[gnu::assume (s)]]; // { dg-error "could not convert 's' from 'S' to 'bool'" } > + return 0; > +} > + > +template > +int > +f6 () > +{ > + T t; > + __attribute__((assume (t))); // { dg-error "could not convert 't' from 'S' to 'bool'" } > + return 0; > +} > + > +int z = f6 (); > --- gcc/testsuite/g++.dg/cpp23/attr-assume5.C.jj 2022-09-22 00:24:01.214176991 +0200 > +++ gcc/testsuite/g++.dg/cpp23/attr-assume5.C 2022-09-22 00:24:01.214176991 +0200 > @@ -0,0 +1,43 @@ > +// P1774R8 - Portable assumptions > +// { dg-do compile { target c++11 } } > +// { dg-options "" } > + > +void > +foo (int x) > +{ > + if (x == 1) > + goto l1; // { dg-message "from here" } > + else if (x == 2) > + goto l2; // { dg-message "from here" } > + else if (x == 3) > + goto l3; // { dg-message "from here" } > + [[assume (({ l0:; if (x == 0) goto l0; true; }))]]; > + [[assume (({ if (x == 0) throw 1; true; }))]]; > + [[assume (({ l1:; true; }))]]; // { dg-error "jump to label 'l1'" } > + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 } > + [[gnu::assume (({ l2:; true; }))]]; // { dg-error "jump to label 'l2'" } > + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 } > + __attribute__((assume (({ l3:; true; })))); // { dg-error "jump to label 'l3'" } > + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 } > + [[assume (({ l4:; true; }))]]; // { dg-error "jump to label 'l4'" } > + [[gnu::assume (({ l5:; true; }))]]; // { dg-error "jump to label 'l5'" } > + __attribute__((assume (({ l6:; true; })))); // { dg-error "jump to label 'l6'" } > + switch (x) > + { > + case 7: > + [[assume (({ case 8:; true; }))]]; // { dg-error "jump to case label" } > + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 } > + [[assume (({ default:; true; }))]]; // { dg-error "jump to case label" } > + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 } > + break; > + } > + if (x == 4) > + goto l4; // { dg-message "from here" } > + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 } > + else if (x == 5) > + goto l5; // { dg-message "from here" } > + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 } > + else if (x == 6) > + goto l6; // { dg-message "from here" } > + // { dg-message "enters 'assume' attribute condition" "" { target *-*-* } .-1 } > +} > > Jakub >