public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++: Implement excess precision support for C++ [PR107097, PR323]
@ 2022-10-11 13:33 Jakub Jelinek
  2022-10-12 18:08 ` Jason Merrill
  2022-10-12 18:17 ` [PATCH] c++: " Marek Polacek
  0 siblings, 2 replies; 5+ messages in thread
From: Jakub Jelinek @ 2022-10-11 13:33 UTC (permalink / raw)
  To: Jason Merrill, Joseph S. Myers; +Cc: gcc-patches

Hi!

The following patch implements excess precision support for C++.
Like for C, it uses EXCESS_PRECISION_EXPR tree to say that its operand
is evaluated in excess precision and what the semantic type of the
expression is.
In most places I've followed what the C FE does in similar spots, so
e.g. for binary ops if one or both operands are already
EXCESS_PRECISION_EXPR, strip those away or for operations that might need
excess precision (+, -, *, /) check if the operands should use excess
precision and convert to that type and at the end wrap into
EXCESS_PRECISION_EXPR with the common semantic type.
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.
There are some cases which needed to be handled differently, the C FE can
just strip EXCESS_PRECISION_EXPR (replace it with its operand) when handling
explicit cast, but that IMHO isn't right for C++ - the discovery what exact
conversion should be used (e.g. if user conversion or standard or their
sequence) should be decided based on the semantic type (i.e. type of
EXCESS_PRECISION_EXPR), and that decision continues in convert_like* where
we pick the right user conversion, again, if say some class has ctor
from double and long double and we are on ia32 with standard excess
precision promoting float/double to long double, then we should pick the
ctor from double.  Or when some other class has ctor from just double,
and EXCESS_PRECISION_EXPR semantic type is float, we should choose the
user ctor from double, but actually just convert the long double excess
precision to double and not to float first.  We need to make sure
even identity conversion converts from excess precision to the semantic one
though, but if identity is chained with other conversions, we don't want
the identity next_conversion to drop to semantic precision only to widen
afterwards.

The existing testcases tweaks were for cases on i686-linux where excess
precision breaks those tests, e.g. if we have
  double d = 4.2;
  if (d == 4.2)
then it does the expected thing only with -fexcess-precision=fast,
because with -fexcess-precision=standard it is actually
  double d = 4.2;
  if ((long double) d == 4.2L)
where 4.2L is different from 4.2.  I've added -fexcess-precision=fast
to some tests and changed other tests to use constants that are exactly
representable and don't suffer from these excess precision issues.

There is one exception, pr68180.C looks like a bug in the patch which is
also present in the C FE (so I'd like to get it resolved incrementally
in both).  Reduced testcase:
typedef float __attribute__((vector_size (16))) float32x4_t;
float32x4_t foo(float32x4_t x, float y) { return x + y; }
with -m32 -std=c11 -Wno-psabi or -m32 -std=c++17 -Wno-psabi
it is rejected with:
pr68180.c:2:52: error: conversion of scalar ‘long double’ to vector ‘float32x4_t’ {aka ‘__vector(4) float’} involves truncation
but without excess precision (say just -std=c11 -Wno-psabi or -std=c++17 -Wno-psabi)
it is accepted.  Perhaps we should pass down the semantic type to
scalar_to_vector and use the semantic type rather than excess precision type
in the diagnostics.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2022-10-11  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/323
	PR c++/107097
gcc/c-family/
	* 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.
	(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 (convert_arguments): For magic 1 remove
	EXCESS_PRECISION_EXPR.
	(cp_build_binary_op): Add excess precision support.
	(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-8.C: New test.
	* g++.target/i386/excess-precision-9.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/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/c-family/c-opts.cc.jj	2022-10-11 10:00:07.108129689 +0200
+++ gcc/c-family/c-opts.cc	2022-10-11 10:06:55.784412531 +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 09:59:39.300518701 +0200
+++ gcc/c-family/c-lex.cc	2022-10-11 10:06:55.784412531 +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-10 11:57:40.158723041 +0200
+++ gcc/cp/parser.cc	2022-10-11 11:56:50.815463848 +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 09:59:39.633514043 +0200
+++ gcc/cp/cvt.cc	2022-10-11 10:06:55.785412518 +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-11 09:59:39.424516967 +0200
+++ gcc/cp/call.cc	2022-10-11 10:06:55.787412490 +0200
@@ -5374,6 +5374,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;
@@ -5407,6 +5408,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);
     }
@@ -5565,6 +5569,46 @@ 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_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 (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
@@ -5572,8 +5616,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
@@ -5937,6 +5979,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);
     }
@@ -6024,9 +6070,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;
 }
@@ -7890,7 +7942,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
@@ -7906,7 +7958,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;
@@ -7983,7 +8035,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,
@@ -7998,13 +8051,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;
 	}
@@ -8124,6 +8179,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
@@ -8163,7 +8220,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)
@@ -8231,7 +8289,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;
 
@@ -8493,7 +8551,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);
@@ -8527,7 +8593,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,
@@ -8550,7 +8616,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;
@@ -8563,7 +8630,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.  */
@@ -8574,7 +8641,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
@@ -8605,6 +8672,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);
     }
@@ -8911,9 +8980,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)
@@ -8932,7 +9001,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;
       }
@@ -9764,7 +9841,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);
@@ -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);
+	  /* 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)))
@@ -13051,7 +13132,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-11 09:59:39.481516169 +0200
+++ gcc/cp/constexpr.cc	2022-10-11 10:06:55.788412476 +0200
@@ -7618,6 +7618,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.  */
@@ -8918,6 +8931,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-11 09:59:39.698513133 +0200
+++ gcc/cp/pt.cc	2022-10-11 10:06:55.792412420 +0200
@@ -17401,6 +17401,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;
+	return build1_loc (EXPR_LOCATION (t), code, type, op0);
+      }
+
     case COMPONENT_REF:
       {
 	tree object;
@@ -20429,6 +20438,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 (input_location, 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-11 09:59:39.512515736 +0200
+++ gcc/cp/cp-tree.h	2022-10-11 10:06:55.793412406 +0200
@@ -6766,6 +6766,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-11 09:59:39.482516155 +0200
+++ gcc/cp/cp-gimplify.cc	2022-10-11 10:06:55.794412392 +0200
@@ -2522,6 +2522,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-11 09:59:39.738512574 +0200
+++ gcc/cp/typeck.cc	2022-10-11 10:24:31.875679017 +0200
@@ -4603,11 +4603,16 @@ 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)
+	    {
+	      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 +5062,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 +5074,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 +5125,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 +5180,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 +5214,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 +5294,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 +5304,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;
@@ -6091,6 +6145,27 @@ 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
+	  && code0 == REAL_TYPE
+	  && code1 == REAL_TYPE
+	  && (extended_float_type_p (orig_type0)
+	      || extended_float_type_p (orig_type1))
+	  && cp_compare_floating_point_conversion_ranks (orig_type0,
+							 orig_type1) == 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 +6256,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 +6345,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 +6443,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 +6517,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 +7246,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 +7267,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 +7368,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 +7383,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 +7399,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 +7563,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 +7579,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 +7974,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 +7993,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 +8006,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 +8293,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 +8376,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 09:59:39.861510854 +0200
+++ gcc/testsuite/gcc.target/i386/excess-precision-1.c	2022-10-11 10:06:55.795412378 +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 09:59:39.877510630 +0200
+++ gcc/testsuite/gcc.target/i386/excess-precision-2.c	2022-10-11 10:06:55.796412364 +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 09:59:39.894510392 +0200
+++ gcc/testsuite/gcc.target/i386/excess-precision-3.c	2022-10-11 10:06:55.796412364 +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 09:59:39.917510070 +0200
+++ gcc/testsuite/gcc.target/i386/excess-precision-7.c	2022-10-11 10:06:55.796412364 +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 09:59:39.947509650 +0200
+++ gcc/testsuite/gcc.target/i386/excess-precision-9.c	2022-10-11 10:06:55.796412364 +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-11 10:06:55.796412364 +0200
+++ gcc/testsuite/g++.target/i386/excess-precision-1.C	2022-10-11 10:06:55.796412364 +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-11 10:06:55.796412364 +0200
+++ gcc/testsuite/g++.target/i386/excess-precision-2.C	2022-10-11 10:06:55.796412364 +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-11 10:06:55.796412364 +0200
+++ gcc/testsuite/g++.target/i386/excess-precision-3.C	2022-10-11 10:06:55.796412364 +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-11 10:06:55.796412364 +0200
+++ gcc/testsuite/g++.target/i386/excess-precision-4.C	2022-10-11 10:06:55.796412364 +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-11 10:06:55.797412351 +0200
+++ gcc/testsuite/g++.target/i386/excess-precision-5.C	2022-10-11 10:06:55.797412351 +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-11 10:06:55.797412351 +0200
+++ gcc/testsuite/g++.target/i386/excess-precision-6.C	2022-10-11 10:06:55.797412351 +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-11 10:06:55.797412351 +0200
+++ gcc/testsuite/g++.target/i386/excess-precision-7.C	2022-10-11 10:06:55.797412351 +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-8.C.jj	2022-10-11 10:06:55.797412351 +0200
+++ gcc/testsuite/g++.target/i386/excess-precision-8.C	2022-10-11 10:06:55.797412351 +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/g++.target/i386/excess-precision-9.C.jj	2022-10-11 10:06:55.797412351 +0200
+++ gcc/testsuite/g++.target/i386/excess-precision-9.C	2022-10-11 10:06:55.797412351 +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/c-c++-common/dfp/convert-bfp-10.c.jj	2020-01-12 11:54:37.003404507 +0100
+++ gcc/testsuite/c-c++-common/dfp/convert-bfp-10.c	2022-10-11 11:20:40.511702027 +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	2020-01-12 11:54:37.003404507 +0100
+++ gcc/testsuite/c-c++-common/dfp/compare-eq-const.c	2022-10-11 11:18:31.432502023 +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	2020-09-03 20:11:34.160825160 +0200
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C	2022-10-11 11:51:31.563910365 +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	2020-01-12 11:54:37.128402621 +0100
+++ gcc/testsuite/g++.dg/cpp1z/decomp12.C	2022-10-11 11:30:05.737825169 +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	2020-01-12 11:54:37.218401263 +0100
+++ gcc/testsuite/g++.dg/other/thunk1.C	2022-10-11 11:30:37.350384645 +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	2020-01-12 11:54:37.279400343 +0100
+++ gcc/testsuite/g++.dg/vect/pr64410.cc	2022-10-11 11:31:54.970302984 +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/cpp1y/pr68180.C.jj	2020-01-12 11:54:37.121402727 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr68180.C	2022-10-11 11:50:42.324596315 +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-05-20 11:45:17.801744787 +0200
+++ gcc/testsuite/g++.dg/cpp0x/variadic-tuple.C	2022-10-11 11:28:34.282099648 +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	2020-01-12 11:54:37.087403240 +0100
+++ gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C	2022-10-11 11:27:50.009716597 +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	2020-01-11 16:31:54.872295939 +0100
+++ gcc/testsuite/g++.old-deja/g++.brendan/copy9.C	2022-10-11 11:10:40.589067853 +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	2020-01-11 16:31:54.877295864 +0100
+++ gcc/testsuite/g++.old-deja/g++.brendan/overload7.C	2022-10-11 11:11:45.238166331 +0200
@@ -1,4 +1,5 @@
 // { dg-do run  }
+// { dg-additional-options "-fexcess-precision=fast" }
 // GROUPS passed overloading
 extern "C" int printf (const char *, ...);
 

	Jakub


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] c++: Implement excess precision support for C++ [PR107097, PR323]
  2022-10-11 13:33 [PATCH] c++: Implement excess precision support for C++ [PR107097, PR323] Jakub Jelinek
@ 2022-10-12 18:08 ` Jason Merrill
  2022-10-13 16:40   ` [PATCH] c++, v2: " Jakub Jelinek
  2022-10-12 18:17 ` [PATCH] c++: " Marek Polacek
  1 sibling, 1 reply; 5+ messages in thread
From: Jason Merrill @ 2022-10-12 18:08 UTC (permalink / raw)
  To: Jakub Jelinek, Joseph S. Myers; +Cc: gcc-patches

On 10/11/22 09:33, Jakub Jelinek wrote:
> Hi!
> 
> The following patch implements excess precision support for C++.

Great!

> Like for C, it uses EXCESS_PRECISION_EXPR tree to say that its operand
> is evaluated in excess precision and what the semantic type of the
> expression is.
> In most places I've followed what the C FE does in similar spots, so
> e.g. for binary ops if one or both operands are already
> EXCESS_PRECISION_EXPR, strip those away or for operations that might need
> excess precision (+, -, *, /) check if the operands should use excess
> precision and convert to that type and at the end wrap into
> EXCESS_PRECISION_EXPR with the common semantic type.
> 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.

> There are some cases which needed to be handled differently, the C FE can
> just strip EXCESS_PRECISION_EXPR (replace it with its operand) when handling
> explicit cast, but that IMHO isn't right for C++ - the discovery what exact
> conversion should be used (e.g. if user conversion or standard or their
> sequence) should be decided based on the semantic type (i.e. type of
> EXCESS_PRECISION_EXPR), and that decision continues in convert_like* where
> we pick the right user conversion, again, if say some class has ctor
> from double and long double and we are on ia32 with standard excess
> precision promoting float/double to long double, then we should pick the
> ctor from double.  Or when some other class has ctor from just double,
> and EXCESS_PRECISION_EXPR semantic type is float, we should choose the
> user ctor from double, but actually just convert the long double excess
> precision to double and not to float first.

That sounds right.

> We need to make sure
> even identity conversion converts from excess precision to the semantic one
> though, but if identity is chained with other conversions, we don't want
> the identity next_conversion to drop to semantic precision only to widen
> afterwards.
> 
> The existing testcases tweaks were for cases on i686-linux where excess
> precision breaks those tests, e.g. if we have
>    double d = 4.2;
>    if (d == 4.2)
> then it does the expected thing only with -fexcess-precision=fast,
> because with -fexcess-precision=standard it is actually
>    double d = 4.2;
>    if ((long double) d == 4.2L)
> where 4.2L is different from 4.2.  I've added -fexcess-precision=fast
> to some tests and changed other tests to use constants that are exactly
> representable and don't suffer from these excess precision issues.
> 
> There is one exception, pr68180.C looks like a bug in the patch which is
> also present in the C FE (so I'd like to get it resolved incrementally
> in both).  Reduced testcase:
> typedef float __attribute__((vector_size (16))) float32x4_t;
> float32x4_t foo(float32x4_t x, float y) { return x + y; }
> with -m32 -std=c11 -Wno-psabi or -m32 -std=c++17 -Wno-psabi
> it is rejected with:
> pr68180.c:2:52: error: conversion of scalar ‘long double’ to vector ‘float32x4_t’ {aka ‘__vector(4) float’} involves truncation
> but without excess precision (say just -std=c11 -Wno-psabi or -std=c++17 -Wno-psabi)
> it is accepted.  Perhaps we should pass down the semantic type to
> scalar_to_vector and use the semantic type rather than excess precision type
> in the diagnostics.

Makes sense.

> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 2022-10-11  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR middle-end/323
> 	PR c++/107097
> gcc/c-family/
> 	* 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.
> 	(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 (convert_arguments): For magic 1 remove
> 	EXCESS_PRECISION_EXPR.
> 	(cp_build_binary_op): Add excess precision support.
> 	(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-8.C: New test.
> 	* g++.target/i386/excess-precision-9.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/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/c-family/c-opts.cc.jj	2022-10-11 10:00:07.108129689 +0200
> +++ gcc/c-family/c-opts.cc	2022-10-11 10:06:55.784412531 +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 09:59:39.300518701 +0200
> +++ gcc/c-family/c-lex.cc	2022-10-11 10:06:55.784412531 +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-10 11:57:40.158723041 +0200
> +++ gcc/cp/parser.cc	2022-10-11 11:56:50.815463848 +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 09:59:39.633514043 +0200
> +++ gcc/cp/cvt.cc	2022-10-11 10:06:55.785412518 +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-11 09:59:39.424516967 +0200
> +++ gcc/cp/call.cc	2022-10-11 10:06:55.787412490 +0200
> @@ -5374,6 +5374,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;
> @@ -5407,6 +5408,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);
>       }
> @@ -5565,6 +5569,46 @@ 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_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)

> +	  && cp_compare_floating_point_conversion_ranks (arg2_type,
> +							 arg3_type) == 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
> @@ -5572,8 +5616,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
> @@ -5937,6 +5979,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);
>       }
> @@ -6024,9 +6070,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;
>   }
> @@ -7890,7 +7942,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
> @@ -7906,7 +7958,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;
> @@ -7983,7 +8035,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,
> @@ -7998,13 +8051,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;
>   	}
> @@ -8124,6 +8179,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
> @@ -8163,7 +8220,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)
> @@ -8231,7 +8289,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;
>   
> @@ -8493,7 +8551,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);
> @@ -8527,7 +8593,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,
> @@ -8550,7 +8616,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;
> @@ -8563,7 +8630,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.  */
> @@ -8574,7 +8641,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
> @@ -8605,6 +8672,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);
>       }
> @@ -8911,9 +8980,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)
> @@ -8932,7 +9001,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;
>         }
> @@ -9764,7 +9841,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);
> @@ -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

  /* Don't truncate excess precision to the semantic type.  */

to clarify.

> +	  /* 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)))
> @@ -13051,7 +13132,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-11 09:59:39.481516169 +0200
> +++ gcc/cp/constexpr.cc	2022-10-11 10:06:55.788412476 +0200
> @@ -7618,6 +7618,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.  */
> @@ -8918,6 +8931,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-11 09:59:39.698513133 +0200
> +++ gcc/cp/pt.cc	2022-10-11 10:06:55.792412420 +0200
> @@ -17401,6 +17401,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;
> +	return build1_loc (EXPR_LOCATION (t), code, type, op0);
> +      }
> +
>       case COMPONENT_REF:
>         {
>   	tree object;
> @@ -20429,6 +20438,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 (input_location, EXCESS_PRECISION_EXPR,

Why not EXPR_LOCATION (t)?

> +			    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-11 09:59:39.512515736 +0200
> +++ gcc/cp/cp-tree.h	2022-10-11 10:06:55.793412406 +0200
> @@ -6766,6 +6766,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-11 09:59:39.482516155 +0200
> +++ gcc/cp/cp-gimplify.cc	2022-10-11 10:06:55.794412392 +0200
> @@ -2522,6 +2522,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-11 09:59:39.738512574 +0200
> +++ gcc/cp/typeck.cc	2022-10-11 10:24:31.875679017 +0200
> @@ -4603,11 +4603,16 @@ 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)
> +	    {
> +	      if (magic == 1 && TREE_CODE (val) == EXCESS_PRECISION_EXPR)
> +		val = TREE_OPERAND (val, 0);

Same magic == 1 issue here.

> +	      /* 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 +5062,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 +5074,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 +5125,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 +5180,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 +5214,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 +5294,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 +5304,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;
> @@ -6091,6 +6145,27 @@ 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
> +	  && code0 == REAL_TYPE
> +	  && code1 == REAL_TYPE
> +	  && (extended_float_type_p (orig_type0)
> +	      || extended_float_type_p (orig_type1))
> +	  && cp_compare_floating_point_conversion_ranks (orig_type0,
> +							 orig_type1) == 3)
> +	{
> +	  if (complain & tf_error)
> +	    {
> +	      rich_location richloc (line_table, location);
> +	      binary_op_error (&richloc, code, type0, type1);
> +	    }
> +	  return error_mark_node;
> +	}

Same error_mark_node issue here.

> +    }
>   
>     if (code == SPACESHIP_EXPR)
>       {
> @@ -6181,6 +6256,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 +6345,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 +6443,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 +6517,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 +7246,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 +7267,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 +7368,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 +7383,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 +7399,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 +7563,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 +7579,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 +7974,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 +7993,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 +8006,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 +8293,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 +8376,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 09:59:39.861510854 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-1.c	2022-10-11 10:06:55.795412378 +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 09:59:39.877510630 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-2.c	2022-10-11 10:06:55.796412364 +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 09:59:39.894510392 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-3.c	2022-10-11 10:06:55.796412364 +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 09:59:39.917510070 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-7.c	2022-10-11 10:06:55.796412364 +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 09:59:39.947509650 +0200
> +++ gcc/testsuite/gcc.target/i386/excess-precision-9.c	2022-10-11 10:06:55.796412364 +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-11 10:06:55.796412364 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-1.C	2022-10-11 10:06:55.796412364 +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-11 10:06:55.796412364 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-2.C	2022-10-11 10:06:55.796412364 +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-11 10:06:55.796412364 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-3.C	2022-10-11 10:06:55.796412364 +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-11 10:06:55.796412364 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-4.C	2022-10-11 10:06:55.796412364 +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-11 10:06:55.797412351 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-5.C	2022-10-11 10:06:55.797412351 +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-11 10:06:55.797412351 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-6.C	2022-10-11 10:06:55.797412351 +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-11 10:06:55.797412351 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-7.C	2022-10-11 10:06:55.797412351 +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-8.C.jj	2022-10-11 10:06:55.797412351 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-8.C	2022-10-11 10:06:55.797412351 +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/g++.target/i386/excess-precision-9.C.jj	2022-10-11 10:06:55.797412351 +0200
> +++ gcc/testsuite/g++.target/i386/excess-precision-9.C	2022-10-11 10:06:55.797412351 +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/c-c++-common/dfp/convert-bfp-10.c.jj	2020-01-12 11:54:37.003404507 +0100
> +++ gcc/testsuite/c-c++-common/dfp/convert-bfp-10.c	2022-10-11 11:20:40.511702027 +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	2020-01-12 11:54:37.003404507 +0100
> +++ gcc/testsuite/c-c++-common/dfp/compare-eq-const.c	2022-10-11 11:18:31.432502023 +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	2020-09-03 20:11:34.160825160 +0200
> +++ gcc/testsuite/g++.dg/cpp1z/constexpr-96862.C	2022-10-11 11:51:31.563910365 +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	2020-01-12 11:54:37.128402621 +0100
> +++ gcc/testsuite/g++.dg/cpp1z/decomp12.C	2022-10-11 11:30:05.737825169 +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	2020-01-12 11:54:37.218401263 +0100
> +++ gcc/testsuite/g++.dg/other/thunk1.C	2022-10-11 11:30:37.350384645 +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	2020-01-12 11:54:37.279400343 +0100
> +++ gcc/testsuite/g++.dg/vect/pr64410.cc	2022-10-11 11:31:54.970302984 +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/cpp1y/pr68180.C.jj	2020-01-12 11:54:37.121402727 +0100
> +++ gcc/testsuite/g++.dg/cpp1y/pr68180.C	2022-10-11 11:50:42.324596315 +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-05-20 11:45:17.801744787 +0200
> +++ gcc/testsuite/g++.dg/cpp0x/variadic-tuple.C	2022-10-11 11:28:34.282099648 +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	2020-01-12 11:54:37.087403240 +0100
> +++ gcc/testsuite/g++.dg/cpp0x/nsdmi-union1.C	2022-10-11 11:27:50.009716597 +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	2020-01-11 16:31:54.872295939 +0100
> +++ gcc/testsuite/g++.old-deja/g++.brendan/copy9.C	2022-10-11 11:10:40.589067853 +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	2020-01-11 16:31:54.877295864 +0100
> +++ gcc/testsuite/g++.old-deja/g++.brendan/overload7.C	2022-10-11 11:11:45.238166331 +0200
> @@ -1,4 +1,5 @@
>   // { dg-do run  }
> +// { dg-additional-options "-fexcess-precision=fast" }
>   // GROUPS passed overloading
>   extern "C" int printf (const char *, ...);
>   
> 
> 	Jakub
> 


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] c++: Implement excess precision support for C++ [PR107097, PR323]
  2022-10-11 13:33 [PATCH] c++: Implement excess precision support for C++ [PR107097, PR323] Jakub Jelinek
  2022-10-12 18:08 ` Jason Merrill
@ 2022-10-12 18:17 ` Marek Polacek
  1 sibling, 0 replies; 5+ messages in thread
From: Marek Polacek @ 2022-10-12 18:17 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Jason Merrill, Joseph S. Myers, gcc-patches

On Tue, Oct 11, 2022 at 03:33:23PM +0200, Jakub Jelinek via Gcc-patches wrote:
> Hi!
> 
> The following patch implements excess precision support for C++.
> Like for C, it uses EXCESS_PRECISION_EXPR tree to say that its operand
> is evaluated in excess precision and what the semantic type of the
> expression is.

One trivial thing: c-common.def says "An EXCESS_PRECISION_EXPR, currently
only used for C and Objective C, ..." which will no longer be accurate with
the patch.

Marek


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH] c++, v2: Implement excess precision support for C++ [PR107097, PR323]
  2022-10-12 18:08 ` Jason Merrill
@ 2022-10-13 16:40   ` Jakub Jelinek
  2022-10-13 19:28     ` Jason Merrill
  0 siblings, 1 reply; 5+ messages in thread
From: Jakub Jelinek @ 2022-10-13 16:40 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Joseph S. Myers, gcc-patches

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;
+	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


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] c++, v2: Implement excess precision support for C++ [PR107097, PR323]
  2022-10-13 16:40   ` [PATCH] c++, v2: " Jakub Jelinek
@ 2022-10-13 19:28     ` Jason Merrill
  0 siblings, 0 replies; 5+ messages in thread
From: Jason Merrill @ 2022-10-13 19:28 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Joseph S. Myers, gcc-patches

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
> 


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-10-13 19:28 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-11 13:33 [PATCH] c++: Implement excess precision support for C++ [PR107097, PR323] 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
2022-10-12 18:17 ` [PATCH] c++: " Marek Polacek

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).