From: Jason Merrill <jason@redhat.com>
To: Jakub Jelinek <jakub@redhat.com>
Cc: "Joseph S. Myers" <joseph@codesourcery.com>, gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++, v2: Implement excess precision support for C++ [PR107097, PR323]
Date: Thu, 13 Oct 2022 15:28:02 -0400 [thread overview]
Message-ID: <c76a957a-c06a-0ce0-2d33-598213a5d041@redhat.com> (raw)
In-Reply-To: <Y0g/g0JYbV33TZiW@tucnak>
On 10/13/22 12:40, Jakub Jelinek wrote:
> On Wed, Oct 12, 2022 at 02:08:20PM -0400, Jason Merrill wrote:
>>> In general I've tried to follow the C99 handling, C11+ relies on the
>>> C standard saying that in case of integral conversions excess precision
>>> can be used (see PR87390 for more details), but I don't see anything similar
>>> on the C++ standard side.
>>
>> https://eel.is/c++draft/expr#pre-6 seems identical to C99 (apart from a
>> stray "the"?); presumably nobody has proposed to copy the N1531
>> clarifications. But since those are clarifications, I'd prefer to use our
>> C11+ semantics to avoid divergence between the default modes of the C and
>> C++ front ends.
>
> Ok, so that it is more readable and say if we decide to make e.g. C++98
> behave like C99 and only C++11 and later like C11, I'm sending this as
> a 2 patch series, this patch is just an updated version of the previous
> patch (your review comments, Marek's mail and missed changes to
> doc/invoke.texi) and another mail will be upgrade of this to the C11
> behavior.
>
>>> + semantic_result_type
>>> + = type_after_usual_arithmetic_conversions (arg2_type, arg3_type);
>>> + if (semantic_result_type == error_mark_node
>>> + && TREE_CODE (arg2_type) == REAL_TYPE
>>> + && TREE_CODE (arg3_type) == REAL_TYPE
>>> + && (extended_float_type_p (arg2_type)
>>> + || extended_float_type_p (arg3_type))
>>
>> What if semantic_result_type is error_mark_node and the other conditions
>> don't hold? That seems impossible, so maybe the other conditions should
>> move into a gcc_checking_assert? (And likewise for result_type below)
>
> Changed in all places to an assert, though previously I missed
> that cp_common_type on complex type(s) could have similar problem.
>
>>> @@ -9772,8 +9849,12 @@ build_over_call (struct z_candidate *can
>>> return error_mark_node;
>>> }
>>> else if (magic != 0)
>>> - /* For other magic varargs only do decay_conversion. */
>>> - a = decay_conversion (a, complain);
>>> + {
>>> + if (magic == 1 && TREE_CODE (a) == EXCESS_PRECISION_EXPR)
>>> + a = TREE_OPERAND (a, 0);
>>
>> It was confusing me that this mentions 1, and the magic_varargs_p comment
>> above mentions 2: Let's add a comment
>
> That is because removing excess precision means keeping
> EXCESS_PRECISION_EXPR around and preserving excess precision
> means removing of EXCESS_PRECISION_EXPR.
>
>>
>> /* Don't truncate excess precision to the semantic type. */
>>
>> to clarify.
>
> Ok.
>
> Here is an updated patch, bootstrapped/regtested on x86_64-linux and
> i686-linux, ok for trunk?
>
> 2022-10-13 Jakub Jelinek <jakub@redhat.com>
>
> PR middle-end/323
> PR c++/107097
> gcc/
> * doc/invoke.texi (-fexcess-precision=standard): Mention that the
> option now also works in C++.
> gcc/c-family/
> * c-common.def (EXCESS_PRECISION_EXPR): Remove comment part about
> the tree being specific to C/ObjC.
> * c-opts.cc (c_common_post_options): Handle flag_excess_precision
> in C++ the same as in C.
> * c-lex.cc (interpret_float): Set const_type to excess_precision ()
> even for C++.
> gcc/cp/
> * parser.cc (cp_parser_primary_expression): Handle
> EXCESS_PRECISION_EXPR with REAL_CST operand the same as REAL_CST.
> * cvt.cc (cp_ep_convert_and_check): New function.
> * call.cc (build_conditional_expr): Add excess precision support.
> When type_after_usual_arithmetic_conversions returns error_mark_node,
> use gcc_checking_assert that it is because of uncomparable floating
> point ranks instead of checking all those conditions and make it
> work also with complex types.
> (convert_like_internal): Likewise. Add NESTED_P argument, pass true
> to recursive calls to convert_like.
> (convert_like): Add NESTED_P argument, pass it through to
> convert_like_internal. For other overload pass false to it.
> (convert_like_with_context): Pass false to NESTED_P.
> (convert_arg_to_ellipsis): Add excess precision support.
> (magic_varargs_p): For __builtin_is{finite,inf,inf_sign,nan,normal}
> and __builtin_fpclassify return 2 instead of 1, document what it
> means.
> (build_over_call): Don't handle former magic 2 which is no longer
> used, instead for magic 1 remove EXCESS_PRECISION_EXPR.
> (perform_direct_initialization_if_possible): Pass false to NESTED_P
> convert_like argument.
> * constexpr.cc (cxx_eval_constant_expression): Handle
> EXCESS_PRECISION_EXPR.
> (potential_constant_expression_1): Likewise.
> * pt.cc (tsubst_copy, tsubst_copy_and_build): Likewise.
> * cp-tree.h (cp_ep_convert_and_check): Declare.
> * cp-gimplify.cc (cp_fold): Handle EXCESS_PRECISION_EXPR.
> * typeck.cc (cp_common_type): For COMPLEX_TYPEs, return error_mark_node
> if recursive call returned it.
> (convert_arguments): For magic 1 remove EXCESS_PRECISION_EXPR.
> (cp_build_binary_op): Add excess precision support. When
> cp_common_type returns error_mark_node, use gcc_checking_assert that
> it is because of uncomparable floating point ranks instead of checking
> all those conditions and make it work also with complex types.
> (cp_build_unary_op): Likewise.
> (cp_build_compound_expr): Likewise.
> (build_static_cast_1): Remove EXCESS_PRECISION_EXPR.
> gcc/testsuite/
> * gcc.target/i386/excess-precision-1.c: For C++ wrap abort and
> exit declarations into extern "C" block.
> * gcc.target/i386/excess-precision-2.c: Likewise.
> * gcc.target/i386/excess-precision-3.c: Likewise. Remove
> check_float_nonproto and check_double_nonproto tests for C++.
> * gcc.target/i386/excess-precision-7.c: For C++ wrap abort and
> exit declarations into extern "C" block.
> * gcc.target/i386/excess-precision-9.c: Likewise.
> * g++.target/i386/excess-precision-1.C: New test.
> * g++.target/i386/excess-precision-2.C: New test.
> * g++.target/i386/excess-precision-3.C: New test.
> * g++.target/i386/excess-precision-4.C: New test.
> * g++.target/i386/excess-precision-5.C: New test.
> * g++.target/i386/excess-precision-6.C: New test.
> * g++.target/i386/excess-precision-7.C: New test.
> * g++.target/i386/excess-precision-9.C: New test.
> * g++.target/i386/excess-precision-11.C: New test.
> * c-c++-common/dfp/convert-bfp-10.c: Add -fexcess-precision=fast
> as dg-additional-options.
> * c-c++-common/dfp/compare-eq-const.c: Likewise.
> * g++.dg/cpp1z/constexpr-96862.C: Likewise.
> * g++.dg/cpp1z/decomp12.C (main): Use 2.25 instead of 2.3 to
> avoid excess precision differences.
> * g++.dg/other/thunk1.C: Add -fexcess-precision=fast
> as dg-additional-options.
> * g++.dg/vect/pr64410.cc: Likewise.
> * g++.dg/cpp1y/pr68180.C: Likewise.
> * g++.dg/vect/pr89653.cc: Likewise.
> * g++.dg/cpp0x/variadic-tuple.C: Likewise.
> * g++.dg/cpp0x/nsdmi-union1.C: Use 4.25 instead of 4.2 to
> avoid excess precision differences.
> * g++.old-deja/g++.brendan/copy9.C: Add -fexcess-precision=fast
> as dg-additional-options.
> * g++.old-deja/g++.brendan/overload7.C: Likewise.
>
> --- gcc/doc/invoke.texi.jj 2022-10-12 22:06:36.029279569 +0200
> +++ gcc/doc/invoke.texi 2022-10-13 16:49:19.313221247 +0200
> @@ -13785,18 +13785,18 @@ default, @option{-fexcess-precision=fast
> operations may be carried out in a wider precision than the types specified
> in the source if that would result in faster code, and it is unpredictable
> when rounding to the types specified in the source code takes place.
> -When compiling C, if @option{-fexcess-precision=standard} is specified then
> -excess precision follows the rules specified in ISO C99; in particular,
> +When compiling C or C++, if @option{-fexcess-precision=standard} is specified
> +then excess precision follows the rules specified in ISO C99 or C++; in particular,
> both casts and assignments cause values to be rounded to their
> semantic types (whereas @option{-ffloat-store} only affects
> -assignments). This option is enabled by default for C if a strict
> -conformance option such as @option{-std=c99} is used.
> +assignments). This option is enabled by default for C or C++ if a strict
> +conformance option such as @option{-std=c99} or @option{-std=c++17} is used.
> @option{-ffast-math} enables @option{-fexcess-precision=fast} by default
> regardless of whether a strict conformance option is used.
>
> @opindex mfpmath
> @option{-fexcess-precision=standard} is not implemented for languages
> -other than C. On the x86, it has no effect if @option{-mfpmath=sse}
> +other than C or C++. On the x86, it has no effect if @option{-mfpmath=sse}
> or @option{-mfpmath=sse+387} is specified; in the former case, IEEE
> semantics apply without excess precision, and in the latter, rounding
> is unpredictable.
> --- gcc/c-family/c-common.def.jj 2022-01-11 22:31:40.595769716 +0100
> +++ gcc/c-family/c-common.def 2022-10-13 16:44:13.408422330 +0200
> @@ -38,10 +38,9 @@ along with GCC; see the file COPYING3.
> not. */
> DEFTREECODE (C_MAYBE_CONST_EXPR, "c_maybe_const_expr", tcc_expression, 2)
>
> -/* An EXCESS_PRECISION_EXPR, currently only used for C and Objective
> - C, represents an expression evaluated in greater range or precision
> - than its type. The type of the EXCESS_PRECISION_EXPR is the
> - semantic type while the operand represents what is actually being
> +/* An EXCESS_PRECISION_EXPR represents an expression evaluated in greater
> + range or precision than its type. The type of the EXCESS_PRECISION_EXPR
> + is the semantic type while the operand represents what is actually being
> evaluated. */
> DEFTREECODE (EXCESS_PRECISION_EXPR, "excess_precision_expr", tcc_expression, 1)
>
> --- gcc/c-family/c-opts.cc.jj 2022-10-11 14:49:41.396226397 +0200
> +++ gcc/c-family/c-opts.cc 2022-10-13 09:35:27.829243895 +0200
> @@ -812,17 +812,9 @@ c_common_post_options (const char **pfil
> C_COMMON_OVERRIDE_OPTIONS;
> #endif
>
> - /* Excess precision other than "fast" requires front-end
> - support. */
> - if (c_dialect_cxx ())
> - {
> - if (flag_excess_precision == EXCESS_PRECISION_STANDARD)
> - sorry ("%<-fexcess-precision=standard%> for C++");
> - flag_excess_precision = EXCESS_PRECISION_FAST;
> - }
> - else if (flag_excess_precision == EXCESS_PRECISION_DEFAULT)
> + if (flag_excess_precision == EXCESS_PRECISION_DEFAULT)
> flag_excess_precision = (flag_iso ? EXCESS_PRECISION_STANDARD
> - : EXCESS_PRECISION_FAST);
> + : EXCESS_PRECISION_FAST);
>
> /* ISO C restricts floating-point expression contraction to within
> source-language expressions (-ffp-contract=on, currently an alias
> --- gcc/c-family/c-lex.cc.jj 2022-10-11 14:49:41.328227329 +0200
> +++ gcc/c-family/c-lex.cc 2022-10-13 09:35:27.900242917 +0200
> @@ -1008,10 +1008,7 @@ interpret_float (const cpp_token *token,
> else
> type = double_type_node;
>
> - if (c_dialect_cxx ())
> - const_type = NULL_TREE;
> - else
> - const_type = excess_precision_type (type);
> + const_type = excess_precision_type (type);
> if (!const_type)
> const_type = type;
>
> --- gcc/cp/parser.cc.jj 2022-10-13 08:40:37.991533161 +0200
> +++ gcc/cp/parser.cc 2022-10-13 09:35:27.912242752 +0200
> @@ -5583,7 +5583,9 @@ cp_parser_primary_expression (cp_parser
> /* Floating-point literals are only allowed in an integral
> constant expression if they are cast to an integral or
> enumeration type. */
> - if (TREE_CODE (token->u.value) == REAL_CST
> + if ((TREE_CODE (token->u.value) == REAL_CST
> + || (TREE_CODE (token->u.value) == EXCESS_PRECISION_EXPR
> + && TREE_CODE (TREE_OPERAND (token->u.value, 0)) == REAL_CST))
> && parser->integral_constant_expression_p
> && pedantic)
> {
> --- gcc/cp/cvt.cc.jj 2022-10-11 14:49:41.705222167 +0200
> +++ gcc/cp/cvt.cc 2022-10-13 09:35:27.956242146 +0200
> @@ -684,6 +684,33 @@ cp_convert_and_check (tree type, tree ex
> return result;
> }
>
> +/* Similarly, but deal with excess precision. SEMANTIC_TYPE is the type this
> + conversion would use without excess precision. If SEMANTIC_TYPE is NULL,
> + this function is equivalent to cp_convert_and_check. This function is
> + a wrapper that handles conversions that may be different than the usual
> + ones because of excess precision. */
> +
> +tree
> +cp_ep_convert_and_check (tree type, tree expr, tree semantic_type,
> + tsubst_flags_t complain)
> +{
> + if (TREE_TYPE (expr) == type)
> + return expr;
> + if (expr == error_mark_node)
> + return expr;
> + if (!semantic_type)
> + return cp_convert_and_check (type, expr, complain);
> +
> + if (TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE
> + && TREE_TYPE (expr) != semantic_type)
> + /* For integers, we need to check the real conversion, not
> + the conversion to the excess precision type. */
> + expr = cp_convert_and_check (semantic_type, expr, complain);
> + /* Result type is the excess precision type, which should be
> + large enough, so do not check. */
> + return cp_convert (type, expr, complain);
> +}
> +
> /* Conversion...
>
> FLAGS indicates how we should behave. */
> --- gcc/cp/call.cc.jj 2022-10-13 08:41:04.735165185 +0200
> +++ gcc/cp/call.cc 2022-10-13 16:01:37.241523281 +0200
> @@ -5359,6 +5359,7 @@ build_conditional_expr (const op_locatio
> tree arg3_type;
> tree result = NULL_TREE;
> tree result_type = NULL_TREE;
> + tree semantic_result_type = NULL_TREE;
> bool is_glvalue = true;
> struct z_candidate *candidates = 0;
> struct z_candidate *cand;
> @@ -5392,6 +5393,9 @@ build_conditional_expr (const op_locatio
> expression, since it needs to be materialized for the
> conversion to bool, so treat it as an xvalue in arg2. */
> arg2 = move (TARGET_EXPR_SLOT (arg1));
> + else if (TREE_CODE (arg1) == EXCESS_PRECISION_EXPR)
> + arg2 = arg1 = build1 (EXCESS_PRECISION_EXPR, TREE_TYPE (arg1),
> + cp_save_expr (TREE_OPERAND (arg1, 0)));
> else
> arg2 = arg1 = cp_save_expr (arg1);
> }
> @@ -5550,6 +5554,52 @@ build_conditional_expr (const op_locatio
> if (error_operand_p (arg1))
> return error_mark_node;
>
> + arg2_type = unlowered_expr_type (arg2);
> + arg3_type = unlowered_expr_type (arg3);
> +
> + if ((TREE_CODE (arg2) == EXCESS_PRECISION_EXPR
> + || TREE_CODE (arg3) == EXCESS_PRECISION_EXPR)
> + && (TREE_CODE (arg2_type) == INTEGER_TYPE
> + || TREE_CODE (arg2_type) == REAL_TYPE
> + || TREE_CODE (arg2_type) == COMPLEX_TYPE)
> + && (TREE_CODE (arg3_type) == INTEGER_TYPE
> + || TREE_CODE (arg3_type) == REAL_TYPE
> + || TREE_CODE (arg3_type) == COMPLEX_TYPE))
> + {
> + semantic_result_type
> + = type_after_usual_arithmetic_conversions (arg2_type, arg3_type);
> + if (semantic_result_type == error_mark_node)
> + {
> + tree t1 = arg2_type;
> + tree t2 = arg3_type;
> + if (TREE_CODE (t1) == COMPLEX_TYPE)
> + t1 = TREE_TYPE (t1);
> + if (TREE_CODE (t2) == COMPLEX_TYPE)
> + t2 = TREE_TYPE (t2);
> + gcc_checking_assert (TREE_CODE (t1) == REAL_TYPE
> + && TREE_CODE (t2) == REAL_TYPE
> + && (extended_float_type_p (t1)
> + || extended_float_type_p (t2))
> + && cp_compare_floating_point_conversion_ranks
> + (t1, t2) == 3);
> + if (complain & tf_error)
> + error_at (loc, "operands to %<?:%> of types %qT and %qT "
> + "have unordered conversion rank",
> + arg2_type, arg3_type);
> + return error_mark_node;
> + }
> + if (TREE_CODE (arg2) == EXCESS_PRECISION_EXPR)
> + {
> + arg2 = TREE_OPERAND (arg2, 0);
> + arg2_type = TREE_TYPE (arg2);
> + }
> + if (TREE_CODE (arg3) == EXCESS_PRECISION_EXPR)
> + {
> + arg3 = TREE_OPERAND (arg3, 0);
> + arg3_type = TREE_TYPE (arg3);
> + }
> + }
> +
> /* [expr.cond]
>
> If either the second or the third operand has type (possibly
> @@ -5557,8 +5607,6 @@ build_conditional_expr (const op_locatio
> array-to-pointer (_conv.array_), and function-to-pointer
> (_conv.func_) standard conversions are performed on the second
> and third operands. */
> - arg2_type = unlowered_expr_type (arg2);
> - arg3_type = unlowered_expr_type (arg3);
> if (VOID_TYPE_P (arg2_type) || VOID_TYPE_P (arg3_type))
> {
> /* 'void' won't help in resolving an overloaded expression on the
> @@ -5850,14 +5898,20 @@ build_conditional_expr (const op_locatio
> /* In this case, there is always a common type. */
> result_type = type_after_usual_arithmetic_conversions (arg2_type,
> arg3_type);
> - if (result_type == error_mark_node
> - && TREE_CODE (arg2_type) == REAL_TYPE
> - && TREE_CODE (arg3_type) == REAL_TYPE
> - && (extended_float_type_p (arg2_type)
> - || extended_float_type_p (arg3_type))
> - && cp_compare_floating_point_conversion_ranks (arg2_type,
> - arg3_type) == 3)
> + if (result_type == error_mark_node)
> {
> + tree t1 = arg2_type;
> + tree t2 = arg3_type;
> + if (TREE_CODE (t1) == COMPLEX_TYPE)
> + t1 = TREE_TYPE (t1);
> + if (TREE_CODE (t2) == COMPLEX_TYPE)
> + t2 = TREE_TYPE (t2);
> + gcc_checking_assert (TREE_CODE (t1) == REAL_TYPE
> + && TREE_CODE (t2) == REAL_TYPE
> + && (extended_float_type_p (t1)
> + || extended_float_type_p (t2))
> + && cp_compare_floating_point_conversion_ranks
> + (t1, t2) == 3);
> if (complain & tf_error)
> error_at (loc, "operands to %<?:%> of types %qT and %qT "
> "have unordered conversion rank",
> @@ -5922,6 +5976,10 @@ build_conditional_expr (const op_locatio
> }
> }
>
> + if (semantic_result_type && INTEGRAL_TYPE_P (arg2_type))
> + arg2 = perform_implicit_conversion (semantic_result_type, arg2, complain);
> + else if (semantic_result_type && INTEGRAL_TYPE_P (arg3_type))
> + arg3 = perform_implicit_conversion (semantic_result_type, arg3, complain);
> arg2 = perform_implicit_conversion (result_type, arg2, complain);
> arg3 = perform_implicit_conversion (result_type, arg3, complain);
> }
> @@ -6009,9 +6067,15 @@ build_conditional_expr (const op_locatio
> /* If this expression is an rvalue, but might be mistaken for an
> lvalue, we must add a NON_LVALUE_EXPR. */
> result = rvalue (result);
> + if (semantic_result_type)
> + result = build1 (EXCESS_PRECISION_EXPR, semantic_result_type,
> + result);
> }
> else
> - result = force_paren_expr (result);
> + {
> + result = force_paren_expr (result);
> + gcc_assert (semantic_result_type == NULL_TREE);
> + }
>
> return result;
> }
> @@ -7875,7 +7939,7 @@ maybe_warn_array_conv (location_t loc, c
> }
>
> /* We call this recursively in convert_like_internal. */
> -static tree convert_like (conversion *, tree, tree, int, bool, bool,
> +static tree convert_like (conversion *, tree, tree, int, bool, bool, bool,
> tsubst_flags_t);
>
> /* Perform the conversions in CONVS on the expression EXPR. FN and
> @@ -7891,7 +7955,7 @@ static tree convert_like (conversion *,
> static tree
> convert_like_internal (conversion *convs, tree expr, tree fn, int argnum,
> bool issue_conversion_warnings, bool c_cast_p,
> - tsubst_flags_t complain)
> + bool nested_p, tsubst_flags_t complain)
> {
> tree totype = convs->type;
> diagnostic_t diag_kind;
> @@ -7968,7 +8032,8 @@ convert_like_internal (conversion *convs
> print_z_candidate (loc, N_("candidate is:"), t->cand);
> expr = convert_like (t, expr, fn, argnum,
> /*issue_conversion_warnings=*/false,
> - /*c_cast_p=*/false, complain);
> + /*c_cast_p=*/false, /*nested_p=*/true,
> + complain);
> if (convs->kind == ck_ref_bind)
> expr = convert_to_reference (totype, expr, CONV_IMPLICIT,
> LOOKUP_NORMAL, NULL_TREE,
> @@ -7983,13 +8048,15 @@ convert_like_internal (conversion *convs
> {
> expr = convert_like (t, expr, fn, argnum,
> /*issue_conversion_warnings=*/false,
> - /*c_cast_p=*/false, complain);
> + /*c_cast_p=*/false, /*nested_p=*/true,
> + complain);
> break;
> }
> else if (t->kind == ck_ambig)
> return convert_like (t, expr, fn, argnum,
> /*issue_conversion_warnings=*/false,
> - /*c_cast_p=*/false, complain);
> + /*c_cast_p=*/false, /*nested_p=*/true,
> + complain);
> else if (t->kind == ck_identity)
> break;
> }
> @@ -8109,6 +8176,8 @@ convert_like_internal (conversion *convs
>
> if (type_unknown_p (expr))
> expr = instantiate_type (totype, expr, complain);
> + if (!nested_p && TREE_CODE (expr) == EXCESS_PRECISION_EXPR)
> + expr = cp_convert (totype, TREE_OPERAND (expr, 0), complain);
> if (expr == null_node
> && INTEGRAL_OR_UNSCOPED_ENUMERATION_TYPE_P (totype))
> /* If __null has been converted to an integer type, we do not want to
> @@ -8148,7 +8217,8 @@ convert_like_internal (conversion *convs
> FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (expr), ix, val)
> {
> tree sub = convert_like (convs->u.list[ix], val, fn,
> - argnum, false, false, complain);
> + argnum, false, false,
> + /*nested_p=*/true, complain);
> if (sub == error_mark_node)
> return sub;
> if (!BRACE_ENCLOSED_INITIALIZER_P (val)
> @@ -8216,7 +8286,7 @@ convert_like_internal (conversion *convs
> expr = convert_like (next_conversion (convs), expr, fn, argnum,
> convs->kind == ck_ref_bind
> ? issue_conversion_warnings : false,
> - c_cast_p, complain & ~tf_no_cleanup);
> + c_cast_p, /*nested_p=*/true, complain & ~tf_no_cleanup);
> if (expr == error_mark_node)
> return error_mark_node;
>
> @@ -8475,7 +8545,15 @@ convert_like_internal (conversion *convs
> return error_mark_node;
>
> warning_sentinel w (warn_zero_as_null_pointer_constant);
> - if (issue_conversion_warnings)
> + if (TREE_CODE (expr) == EXCESS_PRECISION_EXPR)
> + {
> + if (issue_conversion_warnings)
> + expr = cp_ep_convert_and_check (totype, TREE_OPERAND (expr, 0),
> + TREE_TYPE (expr), complain);
> + else
> + expr = cp_convert (totype, TREE_OPERAND (expr, 0), complain);
> + }
> + else if (issue_conversion_warnings)
> expr = cp_convert_and_check (totype, expr, complain);
> else
> expr = cp_convert (totype, expr, complain);
> @@ -8509,7 +8587,7 @@ conv_unsafe_in_template_p (tree to, tree
>
> static tree
> convert_like (conversion *convs, tree expr, tree fn, int argnum,
> - bool issue_conversion_warnings, bool c_cast_p,
> + bool issue_conversion_warnings, bool c_cast_p, bool nested_p,
> tsubst_flags_t complain)
> {
> /* Creating &TARGET_EXPR<> in a template breaks when substituting,
> @@ -8532,7 +8610,8 @@ convert_like (conversion *convs, tree ex
> error_mark_node. */
> }
> expr = convert_like_internal (convs, expr, fn, argnum,
> - issue_conversion_warnings, c_cast_p, complain);
> + issue_conversion_warnings, c_cast_p,
> + nested_p, complain);
> if (expr == error_mark_node)
> return error_mark_node;
> return conv_expr ? conv_expr : expr;
> @@ -8545,7 +8624,7 @@ convert_like (conversion *convs, tree ex
> {
> return convert_like (convs, expr, NULL_TREE, 0,
> /*issue_conversion_warnings=*/true,
> - /*c_cast_p=*/false, complain);
> + /*c_cast_p=*/false, /*nested_p=*/false, complain);
> }
>
> /* Convenience wrapper for convert_like. */
> @@ -8556,7 +8635,7 @@ convert_like_with_context (conversion *c
> {
> return convert_like (convs, expr, fn, argnum,
> /*issue_conversion_warnings=*/true,
> - /*c_cast_p=*/false, complain);
> + /*c_cast_p=*/false, /*nested_p=*/false, complain);
> }
>
> /* ARG is being passed to a varargs function. Perform any conversions
> @@ -8587,6 +8666,8 @@ convert_arg_to_ellipsis (tree arg, tsubs
> "implicit conversion from %qH to %qI when passing "
> "argument to function",
> arg_type, double_type_node);
> + if (TREE_CODE (arg) == EXCESS_PRECISION_EXPR)
> + arg = TREE_OPERAND (arg, 0);
> arg = mark_rvalue_use (arg);
> arg = convert_to_real_nofold (double_type_node, arg);
> }
> @@ -8893,9 +8974,9 @@ convert_for_arg_passing (tree type, tree
> /* Returns non-zero iff FN is a function with magic varargs, i.e. ones for
> which just decay_conversion or no conversions at all should be done.
> This is true for some builtins which don't act like normal functions.
> - Return 2 if no conversions at all should be done, 1 if just
> - decay_conversion. Return 3 for special treatment of the 3rd argument
> - for __builtin_*_overflow_p. */
> + Return 2 if just decay_conversion and removal of excess precision should
> + be done, 1 if just decay_conversion. Return 3 for special treatment of
> + the 3rd argument for __builtin_*_overflow_p. */
>
> int
> magic_varargs_p (tree fn)
> @@ -8914,7 +8995,15 @@ magic_varargs_p (tree fn)
> case BUILT_IN_MUL_OVERFLOW_P:
> return 3;
>
> - default:;
> + case BUILT_IN_ISFINITE:
> + case BUILT_IN_ISINF:
> + case BUILT_IN_ISINF_SIGN:
> + case BUILT_IN_ISNAN:
> + case BUILT_IN_ISNORMAL:
> + case BUILT_IN_FPCLASSIFY:
> + return 2;
> +
> + default:
> return lookup_attribute ("type generic",
> TYPE_ATTRIBUTES (TREE_TYPE (fn))) != 0;
> }
> @@ -9717,7 +9806,7 @@ build_over_call (struct z_candidate *can
> for (; arg_index < vec_safe_length (args); ++arg_index)
> {
> tree a = (*args)[arg_index];
> - if ((magic == 3 && arg_index == 2) || magic == 2)
> + if (magic == 3 && arg_index == 2)
> {
> /* Do no conversions for certain magic varargs. */
> a = mark_type_use (a);
> @@ -9725,8 +9814,13 @@ build_over_call (struct z_candidate *can
> return error_mark_node;
> }
> else if (magic != 0)
> - /* For other magic varargs only do decay_conversion. */
> - a = decay_conversion (a, complain);
> + {
> + /* Don't truncate excess precision to the semantic type. */
> + if (magic == 1 && TREE_CODE (a) == EXCESS_PRECISION_EXPR)
> + a = TREE_OPERAND (a, 0);
> + /* For other magic varargs only do decay_conversion. */
> + a = decay_conversion (a, complain);
> + }
> else if (DECL_CONSTRUCTOR_P (fn)
> && same_type_ignoring_top_level_qualifiers_p (DECL_CONTEXT (fn),
> TREE_TYPE (a)))
> @@ -13004,7 +13098,7 @@ perform_direct_initialization_if_possibl
> else
> expr = convert_like (conv, expr, NULL_TREE, 0,
> /*issue_conversion_warnings=*/false,
> - c_cast_p, complain);
> + c_cast_p, /*nested_p=*/false, complain);
>
> /* Free all the conversions we allocated. */
> obstack_free (&conversion_obstack, p);
> --- gcc/cp/constexpr.cc.jj 2022-10-12 17:51:00.909944772 +0200
> +++ gcc/cp/constexpr.cc 2022-10-13 09:35:27.989241691 +0200
> @@ -7598,6 +7598,19 @@ cxx_eval_constant_expression (const cons
> }
> break;
>
> + case EXCESS_PRECISION_EXPR:
> + {
> + tree oldop = TREE_OPERAND (t, 0);
> +
> + tree op = cxx_eval_constant_expression (ctx, oldop,
> + lval,
> + non_constant_p, overflow_p);
> + if (*non_constant_p)
> + return t;
> + r = fold_convert (TREE_TYPE (t), op);
> + break;
> + }
> +
> case EMPTY_CLASS_EXPR:
> /* Handle EMPTY_CLASS_EXPR produced by build_call_a by lowering
> it to an appropriate CONSTRUCTOR. */
> @@ -8898,6 +8911,9 @@ potential_constant_expression_1 (tree t,
> sub-object of such an object; */
> return RECUR (TREE_OPERAND (t, 0), rval);
>
> + case EXCESS_PRECISION_EXPR:
> + return RECUR (TREE_OPERAND (t, 0), rval);
> +
> case VAR_DECL:
> if (DECL_HAS_VALUE_EXPR_P (t))
> {
> --- gcc/cp/pt.cc.jj 2022-10-13 08:40:38.010532899 +0200
> +++ gcc/cp/pt.cc 2022-10-13 09:46:41.112966988 +0200
> @@ -17412,6 +17412,15 @@ tsubst_copy (tree t, tree args, tsubst_f
> return r;
> }
>
> + case EXCESS_PRECISION_EXPR:
> + {
> + tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> + tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
> + if (TREE_CODE (op0) == EXCESS_PRECISION_EXPR)
> + return op0;
I notice you're assuming this will have the right type, which seems
likely, but I wonder about either checking_asserting that or setting its
type to the one we just tsubsted? OK with or without that change, along
with the followup patch.
> + return build1_loc (EXPR_LOCATION (t), code, type, op0);
> + }
> +
> case COMPONENT_REF:
> {
> tree object;
> @@ -20440,6 +20449,16 @@ tsubst_copy_and_build (tree t,
> templated_operator_saved_lookups (t),
> complain|decltype_flag));
>
> + case EXCESS_PRECISION_EXPR:
> + {
> + tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
> + tree op0 = RECUR (TREE_OPERAND (t, 0));
> + if (TREE_CODE (op0) == EXCESS_PRECISION_EXPR)
> + RETURN (op0);
> + RETURN (build1_loc (EXPR_LOCATION (t), EXCESS_PRECISION_EXPR,
> + type, op0));
> + }
> +
> case FIX_TRUNC_EXPR:
> /* convert_like should have created an IMPLICIT_CONV_EXPR. */
> gcc_unreachable ();
> --- gcc/cp/cp-tree.h.jj 2022-10-13 08:41:04.737165157 +0200
> +++ gcc/cp/cp-tree.h 2022-10-13 09:35:27.999241554 +0200
> @@ -6793,6 +6793,8 @@ extern tree ocp_convert (tree, tree,
> tsubst_flags_t);
> extern tree cp_convert (tree, tree, tsubst_flags_t);
> extern tree cp_convert_and_check (tree, tree, tsubst_flags_t);
> +extern tree cp_ep_convert_and_check (tree, tree, tree,
> + tsubst_flags_t);
> extern tree cp_fold_convert (tree, tree);
> extern tree cp_get_callee (tree);
> extern tree cp_get_callee_fndecl (tree);
> --- gcc/cp/cp-gimplify.cc.jj 2022-10-13 08:40:37.954533670 +0200
> +++ gcc/cp/cp-gimplify.cc 2022-10-13 09:35:28.000241540 +0200
> @@ -2515,6 +2515,11 @@ cp_fold (tree x)
>
> break;
>
> + case EXCESS_PRECISION_EXPR:
> + op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops);
> + x = fold_convert_loc (EXPR_LOCATION (x), TREE_TYPE (x), op0);
> + break;
> +
> case INDIRECT_REF:
> /* We don't need the decltype(auto) obfuscation anymore. */
> if (REF_PARENTHESIZED_P (x))
> --- gcc/cp/typeck.cc.jj 2022-10-13 08:41:04.780164565 +0200
> +++ gcc/cp/typeck.cc 2022-10-13 18:36:08.223309058 +0200
> @@ -439,6 +439,8 @@ cp_common_type (tree t1, tree t2)
> tree subtype
> = type_after_usual_arithmetic_conversions (subtype1, subtype2);
>
> + if (subtype == error_mark_node)
> + return subtype;
> if (code1 == COMPLEX_TYPE && TREE_TYPE (t1) == subtype)
> return build_type_attribute_variant (t1, attributes);
> else if (code2 == COMPLEX_TYPE && TREE_TYPE (t2) == subtype)
> @@ -4603,11 +4605,17 @@ convert_arguments (tree typelist, vec<tr
> }
> else
> {
> - if (fndecl && magic_varargs_p (fndecl))
> - /* Don't do ellipsis conversion for __built_in_constant_p
> - as this will result in spurious errors for non-trivial
> - types. */
> - val = require_complete_type (val, complain);
> + int magic = fndecl ? magic_varargs_p (fndecl) : 0;
> + if (magic)
> + {
> + /* Don't truncate excess precision to the semantic type. */
> + if (magic == 1 && TREE_CODE (val) == EXCESS_PRECISION_EXPR)
> + val = TREE_OPERAND (val, 0);
> + /* Don't do ellipsis conversion for __built_in_constant_p
> + as this will result in spurious errors for non-trivial
> + types. */
> + val = require_complete_type (val, complain);
> + }
> else
> val = convert_arg_to_ellipsis (val, complain);
>
> @@ -5057,7 +5065,7 @@ cp_build_binary_op (const op_location_t
> {
> tree op0, op1;
> enum tree_code code0, code1;
> - tree type0, type1;
> + tree type0, type1, orig_type0, orig_type1;
> const char *invalid_op_diag;
>
> /* Expression code to give to the expression when it is built.
> @@ -5069,6 +5077,10 @@ cp_build_binary_op (const op_location_t
> In the simplest cases this is the common type of the arguments. */
> tree result_type = NULL_TREE;
>
> + /* When the computation is in excess precision, the type of the
> + final EXCESS_PRECISION_EXPR. */
> + tree semantic_result_type = NULL;
> +
> /* Nonzero means operands have already been type-converted
> in whatever way is necessary.
> Zero means they need to be converted to RESULT_TYPE. */
> @@ -5116,6 +5128,10 @@ cp_build_binary_op (const op_location_t
> /* Tree holding instrumentation expression. */
> tree instrument_expr = NULL_TREE;
>
> + /* True means this is an arithmetic operation that may need excess
> + precision. */
> + bool may_need_excess_precision;
> +
> /* Apply default conversions. */
> op0 = resolve_nondeduced_context (orig_op0, complain);
> op1 = resolve_nondeduced_context (orig_op1, complain);
> @@ -5167,8 +5183,8 @@ cp_build_binary_op (const op_location_t
> }
> }
>
> - type0 = TREE_TYPE (op0);
> - type1 = TREE_TYPE (op1);
> + orig_type0 = type0 = TREE_TYPE (op0);
> + orig_type1 = type1 = TREE_TYPE (op1);
>
> /* The expression codes of the data types of the arguments tell us
> whether the arguments are integers, floating, pointers, etc. */
> @@ -5201,6 +5217,47 @@ cp_build_binary_op (const op_location_t
> return error_mark_node;
> }
>
> + switch (code)
> + {
> + case PLUS_EXPR:
> + case MINUS_EXPR:
> + case MULT_EXPR:
> + case TRUNC_DIV_EXPR:
> + case CEIL_DIV_EXPR:
> + case FLOOR_DIV_EXPR:
> + case ROUND_DIV_EXPR:
> + case EXACT_DIV_EXPR:
> + may_need_excess_precision = true;
> + break;
> + default:
> + may_need_excess_precision = false;
> + break;
> + }
> + if (TREE_CODE (op0) == EXCESS_PRECISION_EXPR)
> + {
> + op0 = TREE_OPERAND (op0, 0);
> + type0 = TREE_TYPE (op0);
> + }
> + else if (may_need_excess_precision
> + && (code0 == REAL_TYPE || code0 == COMPLEX_TYPE))
> + if (tree eptype = excess_precision_type (type0))
> + {
> + type0 = eptype;
> + op0 = convert (eptype, op0);
> + }
> + if (TREE_CODE (op1) == EXCESS_PRECISION_EXPR)
> + {
> + op1 = TREE_OPERAND (op1, 0);
> + type1 = TREE_TYPE (op1);
> + }
> + else if (may_need_excess_precision
> + && (code1 == REAL_TYPE || code1 == COMPLEX_TYPE))
> + if (tree eptype = excess_precision_type (type1))
> + {
> + type1 = eptype;
> + op1 = convert (eptype, op1);
> + }
> +
> /* Issue warnings about peculiar, but valid, uses of NULL. */
> if ((null_node_p (orig_op0) || null_node_p (orig_op1))
> /* It's reasonable to use pointer values as operands of &&
> @@ -5240,7 +5297,7 @@ cp_build_binary_op (const op_location_t
> op0 = convert (TREE_TYPE (type1), op0);
> op0 = save_expr (op0);
> op0 = build_vector_from_val (type1, op0);
> - type0 = TREE_TYPE (op0);
> + orig_type0 = type0 = TREE_TYPE (op0);
> code0 = TREE_CODE (type0);
> converted = 1;
> break;
> @@ -5250,7 +5307,7 @@ cp_build_binary_op (const op_location_t
> op1 = convert (TREE_TYPE (type0), op1);
> op1 = save_expr (op1);
> op1 = build_vector_from_val (type0, op1);
> - type1 = TREE_TYPE (op1);
> + orig_type1 = type1 = TREE_TYPE (op1);
> code1 = TREE_CODE (type1);
> converted = 1;
> break;
> @@ -6067,12 +6124,20 @@ cp_build_binary_op (const op_location_t
> && (shorten || common || short_compare))
> {
> result_type = cp_common_type (type0, type1);
> - if (result_type == error_mark_node
> - && code0 == REAL_TYPE
> - && code1 == REAL_TYPE
> - && (extended_float_type_p (type0) || extended_float_type_p (type1))
> - && cp_compare_floating_point_conversion_ranks (type0, type1) == 3)
> + if (result_type == error_mark_node)
> {
> + tree t1 = type0;
> + tree t2 = type1;
> + if (TREE_CODE (t1) == COMPLEX_TYPE)
> + t1 = TREE_TYPE (t1);
> + if (TREE_CODE (t2) == COMPLEX_TYPE)
> + t2 = TREE_TYPE (t2);
> + gcc_checking_assert (TREE_CODE (t1) == REAL_TYPE
> + && TREE_CODE (t2) == REAL_TYPE
> + && (extended_float_type_p (t1)
> + || extended_float_type_p (t2))
> + && cp_compare_floating_point_conversion_ranks
> + (t1, t2) == 3);
> if (complain & tf_error)
> {
> rich_location richloc (line_table, location);
> @@ -6091,6 +6156,33 @@ cp_build_binary_op (const op_location_t
> TREE_TYPE (orig_op1));
> }
> }
> + if (may_need_excess_precision
> + && (orig_type0 != type0 || orig_type1 != type1))
> + {
> + gcc_assert (common);
> + semantic_result_type = cp_common_type (orig_type0, orig_type1);
> + if (semantic_result_type == error_mark_node)
> + {
> + tree t1 = orig_type0;
> + tree t2 = orig_type1;
> + if (TREE_CODE (t1) == COMPLEX_TYPE)
> + t1 = TREE_TYPE (t1);
> + if (TREE_CODE (t2) == COMPLEX_TYPE)
> + t2 = TREE_TYPE (t2);
> + gcc_checking_assert (TREE_CODE (t1) == REAL_TYPE
> + && TREE_CODE (t2) == REAL_TYPE
> + && (extended_float_type_p (t1)
> + || extended_float_type_p (t2))
> + && cp_compare_floating_point_conversion_ranks
> + (t1, t2) == 3);
> + if (complain & tf_error)
> + {
> + rich_location richloc (line_table, location);
> + binary_op_error (&richloc, code, type0, type1);
> + }
> + return error_mark_node;
> + }
> + }
>
> if (code == SPACESHIP_EXPR)
> {
> @@ -6181,6 +6273,8 @@ cp_build_binary_op (const op_location_t
> build_type ? build_type : result_type,
> NULL_TREE, op1);
> TREE_OPERAND (tmp, 0) = op0;
> + if (semantic_result_type)
> + tmp = build1 (EXCESS_PRECISION_EXPR, semantic_result_type, tmp);
> return tmp;
> }
>
> @@ -6268,6 +6362,9 @@ cp_build_binary_op (const op_location_t
> }
> }
> result = build2 (COMPLEX_EXPR, result_type, real, imag);
> + if (semantic_result_type)
> + result = build1 (EXCESS_PRECISION_EXPR, semantic_result_type,
> + result);
> return result;
> }
>
> @@ -6363,9 +6460,11 @@ cp_build_binary_op (const op_location_t
> {
> warning_sentinel w (warn_sign_conversion, short_compare);
> if (!same_type_p (TREE_TYPE (op0), result_type))
> - op0 = cp_convert_and_check (result_type, op0, complain);
> + op0 = cp_ep_convert_and_check (result_type, op0,
> + semantic_result_type, complain);
> if (!same_type_p (TREE_TYPE (op1), result_type))
> - op1 = cp_convert_and_check (result_type, op1, complain);
> + op1 = cp_ep_convert_and_check (result_type, op1,
> + semantic_result_type, complain);
>
> if (op0 == error_mark_node || op1 == error_mark_node)
> return error_mark_node;
> @@ -6435,6 +6534,9 @@ cp_build_binary_op (const op_location_t
> if (resultcode == SPACESHIP_EXPR && !processing_template_decl)
> result = get_target_expr (result, complain);
>
> + if (semantic_result_type)
> + result = build1 (EXCESS_PRECISION_EXPR, semantic_result_type, result);
> +
> if (!c_inhibit_evaluation_warnings)
> {
> if (!processing_template_decl)
> @@ -7161,6 +7263,7 @@ cp_build_unary_op (enum tree_code code,
> tree arg = xarg;
> location_t location = cp_expr_loc_or_input_loc (arg);
> tree argtype = 0;
> + tree eptype = NULL_TREE;
> const char *errstring = NULL;
> tree val;
> const char *invalid_op_diag;
> @@ -7181,6 +7284,12 @@ cp_build_unary_op (enum tree_code code,
> return error_mark_node;
> }
>
> + if (TREE_CODE (arg) == EXCESS_PRECISION_EXPR)
> + {
> + eptype = TREE_TYPE (arg);
> + arg = TREE_OPERAND (arg, 0);
> + }
> +
> switch (code)
> {
> case UNARY_PLUS_EXPR:
> @@ -7276,8 +7385,11 @@ cp_build_unary_op (enum tree_code code,
>
> case REALPART_EXPR:
> case IMAGPART_EXPR:
> - arg = build_real_imag_expr (input_location, code, arg);
> - return arg;
> + val = build_real_imag_expr (input_location, code, arg);
> + if (eptype && TREE_CODE (eptype) == COMPLEX_EXPR)
> + val = build1_loc (input_location, EXCESS_PRECISION_EXPR,
> + TREE_TYPE (eptype), val);
> + return val;
>
> case PREINCREMENT_EXPR:
> case POSTINCREMENT_EXPR:
> @@ -7288,7 +7400,7 @@ cp_build_unary_op (enum tree_code code,
>
> val = unary_complex_lvalue (code, arg);
> if (val != 0)
> - return val;
> + goto return_build_unary_op;
>
> arg = mark_lvalue_use (arg);
>
> @@ -7304,8 +7416,8 @@ cp_build_unary_op (enum tree_code code,
> real = cp_build_unary_op (code, real, true, complain);
> if (real == error_mark_node || imag == error_mark_node)
> return error_mark_node;
> - return build2 (COMPLEX_EXPR, TREE_TYPE (arg),
> - real, imag);
> + val = build2 (COMPLEX_EXPR, TREE_TYPE (arg), real, imag);
> + goto return_build_unary_op;
> }
>
> /* Report invalid types. */
> @@ -7468,7 +7580,7 @@ cp_build_unary_op (enum tree_code code,
> val = build2 (code, TREE_TYPE (arg), arg, inc);
>
> TREE_SIDE_EFFECTS (val) = 1;
> - return val;
> + goto return_build_unary_op;
> }
>
> case ADDR_EXPR:
> @@ -7484,7 +7596,11 @@ cp_build_unary_op (enum tree_code code,
> {
> if (argtype == 0)
> argtype = TREE_TYPE (arg);
> - return build1 (code, argtype, arg);
> + val = build1 (code, argtype, arg);
> + return_build_unary_op:
> + if (eptype)
> + val = build1 (EXCESS_PRECISION_EXPR, eptype, val);
> + return val;
> }
>
> if (complain & tf_error)
> @@ -7875,6 +7991,15 @@ cp_build_compound_expr (tree lhs, tree r
> if (lhs == error_mark_node || rhs == error_mark_node)
> return error_mark_node;
>
> + if (TREE_CODE (lhs) == EXCESS_PRECISION_EXPR)
> + lhs = TREE_OPERAND (lhs, 0);
> + tree eptype = NULL_TREE;
> + if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR)
> + {
> + eptype = TREE_TYPE (rhs);
> + rhs = TREE_OPERAND (rhs, 0);
> + }
> +
> if (TREE_CODE (rhs) == TARGET_EXPR)
> {
> /* If the rhs is a TARGET_EXPR, then build the compound
> @@ -7885,6 +8010,8 @@ cp_build_compound_expr (tree lhs, tree r
> init = build2 (COMPOUND_EXPR, TREE_TYPE (init), lhs, init);
> TREE_OPERAND (rhs, 1) = init;
>
> + if (eptype)
> + rhs = build1 (EXCESS_PRECISION_EXPR, eptype, rhs);
> return rhs;
> }
>
> @@ -7896,7 +8023,10 @@ cp_build_compound_expr (tree lhs, tree r
> return error_mark_node;
> }
>
> - return build2 (COMPOUND_EXPR, TREE_TYPE (rhs), lhs, rhs);
> + tree ret = build2 (COMPOUND_EXPR, TREE_TYPE (rhs), lhs, rhs);
> + if (eptype)
> + ret = build1 (EXCESS_PRECISION_EXPR, eptype, ret);
> + return ret;
> }
>
> /* Issue a diagnostic message if casting from SRC_TYPE to DEST_TYPE
> @@ -8180,7 +8310,11 @@ build_static_cast_1 (location_t loc, tre
>
> Any expression can be explicitly converted to type cv void. */
> if (VOID_TYPE_P (type))
> - return convert_to_void (expr, ICV_CAST, complain);
> + {
> + if (TREE_CODE (expr) == EXCESS_PRECISION_EXPR)
> + expr = TREE_OPERAND (expr, 0);
> + return convert_to_void (expr, ICV_CAST, complain);
> + }
>
> /* [class.abstract]
> An abstract class shall not be used ... as the type of an explicit
> @@ -8259,6 +8393,8 @@ build_static_cast_1 (location_t loc, tre
> {
> if (processing_template_decl)
> return expr;
> + if (TREE_CODE (expr) == EXCESS_PRECISION_EXPR)
> + expr = TREE_OPERAND (expr, 0);
> return ocp_convert (type, expr, CONV_C_CAST, LOOKUP_NORMAL, complain);
> }
>
> --- gcc/testsuite/gcc.target/i386/excess-precision-1.c.jj 2022-10-11 14:49:42.367213104 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-1.c 2022-10-13 09:35:28.016241320 +0200
> @@ -5,8 +5,14 @@
>
> #include <float.h>
>
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> extern void abort (void);
> extern void exit (int);
> +#ifdef __cplusplus
> +}
> +#endif
>
> volatile float f1 = 1.0f;
> volatile float f2 = 0x1.0p-30f;
> --- gcc/testsuite/gcc.target/i386/excess-precision-2.c.jj 2022-10-11 14:49:42.439212118 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-2.c 2022-10-13 09:35:28.037241031 +0200
> @@ -4,8 +4,14 @@
>
> #include <float.h>
>
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> extern void abort (void);
> extern void exit (int);
> +#ifdef __cplusplus
> +}
> +#endif
>
> volatile long double ldadd1 = 1.0l + 0x1.0p-30l;
> volatile long double ld11f = 1.1f;
> --- gcc/testsuite/gcc.target/i386/excess-precision-3.c.jj 2022-10-11 14:49:42.456211885 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-3.c 2022-10-13 09:35:28.054240796 +0200
> @@ -6,8 +6,14 @@
> #include <float.h>
> #include <stdarg.h>
>
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> extern void abort (void);
> extern void exit (int);
> +#ifdef __cplusplus
> +}
> +#endif
>
> volatile float f1 = 1.0f;
> volatile float f2 = 0x1.0p-30f;
> @@ -100,6 +106,7 @@ check_double (double d)
> abort ();
> }
>
> +#ifndef __cplusplus
> static inline void
> check_float_nonproto (f)
> float f;
> @@ -115,6 +122,7 @@ check_double_nonproto (d)
> if (d != dadd2)
> abort ();
> }
> +#endif
>
> static void
> check_double_va (int i, ...)
> @@ -132,9 +140,11 @@ test_call (void)
> check_float (f1 + f2);
> check_double (d1 + d2 + d3);
> check_double (f1 + f2 + f3);
> +#ifndef __cplusplus
> check_float_nonproto (f1 + f2);
> check_double_nonproto (d1 + d2 + d3);
> check_double_nonproto (f1 + f2 + f3);
> +#endif
> check_double_va (0, d1 + d2 + d3);
> check_double_va (0, f1 + f2 + f3);
> }
> --- gcc/testsuite/gcc.target/i386/excess-precision-7.c.jj 2022-10-11 14:49:42.456211885 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-7.c 2022-10-13 09:35:28.070240576 +0200
> @@ -4,8 +4,14 @@
> /* { dg-do run } */
> /* { dg-options "-std=c99 -mfpmath=387 -fexcess-precision=standard" } */
>
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> extern void abort (void);
> extern void exit (int);
> +#ifdef __cplusplus
> +}
> +#endif
>
> int
> main (void)
> --- gcc/testsuite/gcc.target/i386/excess-precision-9.c.jj 2022-10-11 14:49:42.542210707 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-9.c 2022-10-13 09:35:28.078240466 +0200
> @@ -3,8 +3,14 @@
> /* { dg-do run } */
> /* { dg-options "-std=c99 -mfpmath=387 -fexcess-precision=standard" } */
>
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> extern void abort (void);
> extern void exit (int);
> +#ifdef __cplusplus
> +}
> +#endif
>
> int
> main (void)
> --- gcc/testsuite/g++.target/i386/excess-precision-1.C.jj 2022-10-13 09:35:28.078240466 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-1.C 2022-10-13 09:35:28.078240466 +0200
> @@ -0,0 +1,6 @@
> +// Excess precision tests. Test that excess precision is carried
> +// through various operations.
> +// { dg-do run }
> +// { dg-options "-O2 -mfpmath=387 -fexcess-precision=standard" }
> +
> +#include "../../gcc.target/i386/excess-precision-1.c"
> --- gcc/testsuite/g++.target/i386/excess-precision-2.C.jj 2022-10-13 09:35:28.079240452 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-2.C 2022-10-13 09:35:28.079240452 +0200
> @@ -0,0 +1,5 @@
> +// Excess precision tests. Test excess precision of constants.
> +// { dg-do run }
> +// { dg-options "-O2 -mfpmath=387 -fexcess-precision=standard" }
> +
> +#include "../../gcc.target/i386/excess-precision-2.c"
> --- gcc/testsuite/g++.target/i386/excess-precision-3.C.jj 2022-10-13 09:35:28.079240452 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-3.C 2022-10-13 09:35:28.079240452 +0200
> @@ -0,0 +1,6 @@
> +// Excess precision tests. Test excess precision is removed when
> +// necessary.
> +// { dg-do run }
> +// { dg-options "-O2 -mfpmath=387 -fexcess-precision=standard" }
> +
> +#include "../../gcc.target/i386/excess-precision-3.c"
> --- gcc/testsuite/g++.target/i386/excess-precision-4.C.jj 2022-10-13 09:35:28.079240452 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-4.C 2022-10-13 09:35:28.079240452 +0200
> @@ -0,0 +1,7 @@
> +// Excess precision tests. Test diagnostics for excess precision of
> +// constants.
> +// { dg-do compile }
> +// { dg-options "-mfpmath=387 -fexcess-precision=standard" }
> +
> +float f = 0.0f * 1e50f; // { dg-warning "floating constant exceeds range of 'float'" }
> +double d = 0.0 * 1e400; // { dg-warning "floating constant exceeds range of 'double'" }
> --- gcc/testsuite/g++.target/i386/excess-precision-5.C.jj 2022-10-13 09:35:28.079240452 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-5.C 2022-10-13 09:35:28.079240452 +0200
> @@ -0,0 +1,32 @@
> +// Excess precision tests. Verify excess precision doesn't affect
> +// actual types.
> +// { dg-do compile { target c++11 } }
> +// { dg-options "-mfpmath=387 -fexcess-precision=standard" }
> +
> +namespace std {
> + template<typename T, T v> struct integral_constant {
> + static constexpr T value = v;
> + };
> + typedef integral_constant<bool, false> false_type;
> + typedef integral_constant<bool, true> true_type;
> + template<class T, class U>
> + struct is_same : std::false_type {};
> + template <class T>
> + struct is_same<T, T> : std::true_type {};
> +}
> +
> +float f;
> +double d;
> +
> +void
> +test_types (void)
> +{
> +#define CHECK_FLOAT(E) static_assert (std::is_same <float, decltype (E)>::value, "")
> +#define CHECK_DOUBLE(E) static_assert (std::is_same <double, decltype (E)>::value, "")
> + CHECK_FLOAT (f + f);
> + CHECK_DOUBLE (d + d);
> + CHECK_FLOAT (f * f / f);
> + CHECK_DOUBLE (d * d / d);
> + CHECK_FLOAT (f ? f - f : f);
> + CHECK_DOUBLE (d ? d - d : d);
> +}
> --- gcc/testsuite/g++.target/i386/excess-precision-6.C.jj 2022-10-13 09:35:28.080240438 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-6.C 2022-10-13 09:35:28.080240438 +0200
> @@ -0,0 +1,19 @@
> +// Excess precision tests. Make sure sqrt is not inlined for float or
> +// double.
> +// { dg-do compile }
> +// { dg-options "-mfpmath=387 -O2 -fno-math-errno -fexcess-precision=standard" }
> +
> +float f;
> +double d;
> +
> +float fr;
> +double dr;
> +
> +void
> +test_builtins (void)
> +{
> + fr = __builtin_sqrtf (f);
> + dr = __builtin_sqrt (d);
> +}
> +
> +// { dg-final { scan-assembler-not "fsqrt" } }
> --- gcc/testsuite/g++.target/i386/excess-precision-7.C.jj 2022-10-13 09:35:28.080240438 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-7.C 2022-10-13 09:35:28.080240438 +0200
> @@ -0,0 +1,7 @@
> +// Excess precision tests. Test C99 semantics for conversions from
> +// integers to floating point: no excess precision for either explicit
> +// or implicit conversions.
> +// { dg-do run }
> +// { dg-options "-mfpmath=387 -fexcess-precision=standard" }
> +
> +#include "../../gcc.target/i386/excess-precision-7.c"
> --- gcc/testsuite/g++.target/i386/excess-precision-9.C.jj 2022-10-13 09:35:28.080240438 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-9.C 2022-10-13 09:35:28.080240438 +0200
> @@ -0,0 +1,6 @@
> +// Excess precision tests. Test implicit conversions in comparisons:
> +// no excess precision in C++.
> +// { dg-do run }
> +// { dg-options "-mfpmath=387 -fexcess-precision=standard" }
> +
> +#include "../../gcc.target/i386/excess-precision-9.c"
> --- gcc/testsuite/g++.target/i386/excess-precision-11.C.jj 2022-10-13 09:35:28.080240438 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-11.C 2022-10-13 09:35:28.080240438 +0200
> @@ -0,0 +1,105 @@
> +// Excess precision tests. Test excess precision is removed when
> +// necessary.
> +// { dg-do run }
> +// { dg-options "-O2 -mfpmath=387 -fexcess-precision=standard" }
> +
> +#include <float.h>
> +#include <stdarg.h>
> +
> +extern "C" void abort ();
> +
> +volatile float f1 = 1.0f;
> +volatile float f2 = 0x1.0p-30f;
> +volatile float f3 = 0x1.0p-60f;
> +volatile double d1 = 1.0;
> +volatile double d2 = 0x1.0p-30;
> +volatile double d3 = 0x1.0p-60;
> +volatile double d3d = 0x1.0p-52;
> +volatile float fadd1 = 1.0f + 0x1.0p-30f;
> +volatile double dadd2 = 1.0 + 0x1.0p-30 + 0x1.0p-60;
> +volatile double dh = 0x1.0p-24;
> +volatile float fha = 1.0f + 0x1.0p-23f;
> +
> +static inline void
> +check_float (float f)
> +{
> + if (f != fadd1)
> + abort ();
> +}
> +
> +static inline void
> +check_float (double)
> +{
> + abort ();
> +}
> +
> +static inline void
> +check_float (long double)
> +{
> + abort ();
> +}
> +
> +static inline void
> +check_double (double d)
> +{
> + if (d != dadd2)
> + abort ();
> +}
> +
> +static inline void
> +check_double (long double)
> +{
> + abort ();
> +}
> +
> +static inline void
> +check_float2 (float f)
> +{
> + if (f != fha)
> + abort ();
> +}
> +
> +struct S {
> + S () {}
> + S (float f) { if (f != fadd1) abort (); }
> +};
> +
> +struct T {
> + T () {}
> + T (double d) { if (d != dadd2) abort (); }
> +};
> +
> +static inline void
> +check_float3 (S)
> +{
> +}
> +
> +static inline void
> +check_double2 (T)
> +{
> +}
> +
> +void
> +test_call ()
> +{
> + check_float (f1 + f2);
> + check_double (f1 + f2);
> + check_double (d1 + d2 + d3);
> + /* Verify rounding direct to float without double rounding. */
> + if (sizeof (long double) > sizeof (double))
> + check_float2 (d1 + dh + d3);
> + else
> + check_float2 (d1 + dh + d3d);
> + check_float3 (f1 + f2);
> + check_double2 (f1 + f2);
> + check_double2 (d1 + d2 + d3);
> + S s1 = static_cast<S> (f1 + f2);
> + T t2 = static_cast<T> (f1 + f2);
> + T t3 = static_cast<T> (d1 + d2 + d3);
> +}
> +
> +int
> +main ()
> +{
> + test_call ();
> +}
> --- gcc/testsuite/c-c++-common/dfp/convert-bfp-10.c.jj 2022-10-11 14:49:42.046217498 +0200
> +++ gcc/testsuite/c-c++-common/dfp/convert-bfp-10.c 2022-10-13 09:35:28.092240273 +0200
> @@ -1,4 +1,5 @@
> /* This test assumes IEEE float and double. */
> +/* { dg-additional-options "-fexcess-precision=fast" } */
>
> #include "convert.h"
>
> --- gcc/testsuite/c-c++-common/dfp/compare-eq-const.c.jj 2022-10-11 14:49:42.024217800 +0200
> +++ gcc/testsuite/c-c++-common/dfp/compare-eq-const.c 2022-10-13 09:35:28.104240108 +0200
> @@ -1,5 +1,6 @@
> /* C99 6.5.9 Equality operators.
> Compare decimal float constants against each other. */
> +/* { dg-additional-options "-fexcess-precision=fast" } */
>
> #include "dfp-dbg.h"
>
> --- gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C.jj 2022-10-11 14:49:42.188215554 +0200
> +++ gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C 2022-10-13 09:35:28.112239998 +0200
> @@ -1,6 +1,6 @@
> // PR c++/96862
> // { dg-do compile { target c++17 } }
> -// { dg-additional-options "-frounding-math" }
> +// { dg-additional-options "-frounding-math -fexcess-precision=fast" }
>
> constexpr double a = 0x1.0p+100 + 0x1.0p-100;
> const double b = 0x1.0p+100 + 0x1.0p-100;
> --- gcc/testsuite/g++.dg/cpp1z/decomp12.C.jj 2022-10-11 14:49:42.193215485 +0200
> +++ gcc/testsuite/g++.dg/cpp1z/decomp12.C 2022-10-13 09:35:28.113239984 +0200
> @@ -7,13 +7,13 @@ template <typename, typename> struct sam
> template <typename T> struct same_type<T, T> {};
>
> int main() {
> - std::tuple tuple = { 1, 'a', 2.3, true };
> + std::tuple tuple = { 1, 'a', 2.25, true };
> auto[i, c, d, b] = tuple;
> same_type<std::tuple_element<0, decltype(tuple)>::type, decltype(i)>{};
> same_type<decltype(i), int>{};
> same_type<decltype(c), char>{};
> same_type<decltype(d), double>{};
> same_type<decltype(b), bool>{};
> - if (i != 1 || c != 'a' || d != 2.3 || b != true)
> + if (i != 1 || c != 'a' || d != 2.25 || b != true)
> __builtin_abort ();
> }
> --- gcc/testsuite/g++.dg/other/thunk1.C.jj 2022-10-11 14:49:42.224215061 +0200
> +++ gcc/testsuite/g++.dg/other/thunk1.C 2022-10-13 09:35:28.120239887 +0200
> @@ -1,5 +1,6 @@
> // PR c++/12007 Multiple inheritance float pass by value fails
> // { dg-do run }
> +// { dg-additional-options "-fexcess-precision=fast" }
>
> extern "C" void abort (void);
>
> --- gcc/testsuite/g++.dg/vect/pr64410.cc.jj 2022-10-11 14:49:42.264214513 +0200
> +++ gcc/testsuite/g++.dg/vect/pr64410.cc 2022-10-13 09:35:28.121239874 +0200
> @@ -1,5 +1,6 @@
> // { dg-do compile }
> // { dg-require-effective-target vect_double }
> +// { dg-additional-options "-fexcess-precision=fast" }
>
> #include <vector>
> #include <complex>
> --- gcc/testsuite/g++.dg/vect/pr89653.cc.jj 2020-01-12 11:54:37.280400328 +0100
> +++ gcc/testsuite/g++.dg/vect/pr89653.cc 2022-10-13 10:01:35.564649879 +0200
> @@ -1,5 +1,6 @@
> // { dg-do compile }
> // { dg-require-effective-target vect_double }
> +// { dg-additional-options "-fexcess-precision=fast" }
>
> #include <algorithm>
>
> --- gcc/testsuite/g++.dg/cpp1y/pr68180.C.jj 2022-10-11 14:49:42.168215827 +0200
> +++ gcc/testsuite/g++.dg/cpp1y/pr68180.C 2022-10-13 09:35:28.121239874 +0200
> @@ -1,6 +1,6 @@
> // PR c++/68180
> // { dg-do compile { target c++14 } }
> -// { dg-additional-options "-Wno-psabi" }
> +// { dg-additional-options "-Wno-psabi -fexcess-precision=fast" }
>
> typedef float __attribute__( ( vector_size( 16 ) ) ) float32x4_t;
> constexpr float32x4_t fill(float x) {
> --- gcc/testsuite/g++.dg/cpp0x/variadic-tuple.C.jj 2022-10-11 14:49:42.135216279 +0200
> +++ gcc/testsuite/g++.dg/cpp0x/variadic-tuple.C 2022-10-13 09:35:28.130239750 +0200
> @@ -1,4 +1,5 @@
> // { dg-do run { target c++11 } }
> +// { dg-additional-options "-fexcess-precision=fast" }
> // An implementation of TR1's <tuple> using variadic teplates
> // Contributed by Douglas Gregor <doug.gregor@gmail.com>
>
> --- gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C.jj 2022-10-11 14:49:42.104216704 +0200
> +++ gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C 2022-10-13 09:35:28.140239612 +0200
> @@ -18,8 +18,8 @@ int main()
> {
> Test t;
> B b;
> - B b2(4.2);
> + B b2(4.25);
>
> - if (t.a != 4 || b.i != 42 || b2.d != 4.2)
> + if (t.a != 4 || b.i != 42 || b2.d != 4.25)
> __builtin_abort();
> }
> --- gcc/testsuite/g++.old-deja/g++.brendan/copy9.C.jj 2022-10-11 14:49:42.304213966 +0200
> +++ gcc/testsuite/g++.old-deja/g++.brendan/copy9.C 2022-10-13 09:35:28.152239447 +0200
> @@ -1,4 +1,5 @@
> // { dg-do run }
> +// { dg-additional-options "-fexcess-precision=fast" }
> // GROUPS passed copy-ctors
> #include <iostream>
>
> --- gcc/testsuite/g++.old-deja/g++.brendan/overload7.C.jj 2022-10-11 14:49:42.327213651 +0200
> +++ gcc/testsuite/g++.old-deja/g++.brendan/overload7.C 2022-10-13 09:35:28.152239447 +0200
> @@ -1,4 +1,5 @@
> // { dg-do run }
> +// { dg-additional-options "-fexcess-precision=fast" }
> // GROUPS passed overloading
> extern "C" int printf (const char *, ...);
>
>
>
> Jakub
>
next prev parent reply other threads:[~2022-10-13 19:28 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-10-11 13:33 [PATCH] c++: " Jakub Jelinek
2022-10-12 18:08 ` Jason Merrill
2022-10-13 16:40 ` [PATCH] c++, v2: " Jakub Jelinek
2022-10-13 19:28 ` Jason Merrill [this message]
2022-10-12 18:17 ` [PATCH] c++: " Marek Polacek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=c76a957a-c06a-0ce0-2d33-598213a5d041@redhat.com \
--to=jason@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jakub@redhat.com \
--cc=joseph@codesourcery.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).