This patch adds __builtin_complex to support generating values with
arbitrary real and imaginary parts, including in static initializers,
despite the absence of imaginary types.  (Recall that X + I * Y, in
the absence of imaginary types, is really X + Y * (0.0 + 1.0I),
resulting in a real part X + Y * 0.0 which yields incorrect results
when infinities or signed zero are used.)  This is intended to be used
by C library headers to define the C1X macros CMPLX*, with definitions
along the lines of:

#define CMPLX(X, Y) __builtin_complex ((double) (X), (double) (Y))

As requested in PR 48760 comment 12, this is purely a C front-end
built-in (actually, a keyword that looks like a built-in function)
providing syntax for COMPLEX_EXPR, rather than a middle-end built-in
function.  The C++ front end is using a different approach for this
issue, allowing list-initialization of _Complex values.  (Allowing
{ real, imag } initializers was one of the approaches considered for
C1X before the final macro approach was arrived at.  See N1464 for the
approach that was followed and references to previous proposals; the
initializer approach was proposal two in N1431.  Note that if you did
allow such initializers for C, it wouldn't provide *expressions*
usable in static initializers, since to make a braced initializer into
an expression you need a compound literal and compound literals can't
be used in static initializers.)

Bootstrapped with no regressions on x86_64-unknown-linux-gnu.  Applied
to mainline.

2011-08-19  Joseph Myers  <jos...@codesourcery.com>

        * c-parser.c (c_parser_postfix_expression): Handle
        RID_BUILTIN_COMPLEX.
        * doc/extend.texi (__builtin_complex): Document.

c-family:
2011-08-19  Joseph Myers  <jos...@codesourcery.com>

        * c-common.c (c_common_reswords): Add __builtin_complex.
        * c-common.h (RID_BUILTIN_COMPLEX): New.

testsuite:
2011-08-19  Joseph Myers  <jos...@codesourcery.com>

        * gcc.dg/builtin-complex-err-1.c, gcc.dg/builtin-complex-err-2.c,
        gcc.dg/dfp/builtin-complex.c, gcc.dg/torture/builtin-complex-1.c:
        New tests.

Index: gcc/doc/extend.texi
===================================================================
--- gcc/doc/extend.texi (revision 177894)
+++ gcc/doc/extend.texi (working copy)
@@ -7511,6 +7511,18 @@ future revisions.
 
 @end deftypefn
 
+@deftypefn {Built-in Function} @var{type} __builtin_complex (@var{real}, 
@var{imag})
+
+The built-in function @code{__builtin_complex} is provided for use in
+implementing the ISO C1X macros @code{CMPLXF}, @code{CMPLX} and
+@code{CMPLXL}.  @var{real} and @var{imag} must have the same type, a
+real binary floating-point type, and the result has the corresponding
+complex type with real and imaginary parts @var{real} and @var{imag}.
+Unlike @samp{@var{real} + I * @var{imag}}, this works even when
+infinities, NaNs and negative zeros are involved.
+
+@end deftypefn
+
 @deftypefn {Built-in Function} int __builtin_constant_p (@var{exp})
 You can use the built-in function @code{__builtin_constant_p} to
 determine if a value is known to be constant at compile-time and hence
Index: gcc/c-family/c-common.c
===================================================================
--- gcc/c-family/c-common.c     (revision 177894)
+++ gcc/c-family/c-common.c     (working copy)
@@ -424,6 +424,7 @@ const struct c_common_resword c_common_r
   { "__attribute",     RID_ATTRIBUTE,  0 },
   { "__attribute__",   RID_ATTRIBUTE,  0 },
   { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY },
+  { "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
   { "__builtin_offsetof", RID_OFFSETOF, 0 },
   { "__builtin_types_compatible_p", RID_TYPES_COMPATIBLE_P, D_CONLY },
   { "__builtin_va_arg",        RID_VA_ARG,     0 },
Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h     (revision 177894)
+++ gcc/c-family/c-common.h     (working copy)
@@ -103,7 +103,7 @@ enum rid
   /* C extensions */
   RID_ASM,       RID_TYPEOF,   RID_ALIGNOF,  RID_ATTRIBUTE,  RID_VA_ARG,
   RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,      RID_CHOOSE_EXPR,
-  RID_TYPES_COMPATIBLE_P,
+  RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,
   RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
   RID_FRACT, RID_ACCUM,
 
Index: gcc/testsuite/gcc.dg/dfp/builtin-complex.c
===================================================================
--- gcc/testsuite/gcc.dg/dfp/builtin-complex.c  (revision 0)
+++ gcc/testsuite/gcc.dg/dfp/builtin-complex.c  (revision 0)
@@ -0,0 +1,10 @@
+/* Test __builtin_complex errors with DFP.  */
+/* { dg-do compile } */
+
+_Decimal32 a, b;
+
+void
+f (void)
+{
+  __builtin_complex (a, b); /* { dg-error "not of real binary floating-point 
type" } */
+}
Index: gcc/testsuite/gcc.dg/builtin-complex-err-1.c
===================================================================
--- gcc/testsuite/gcc.dg/builtin-complex-err-1.c        (revision 0)
+++ gcc/testsuite/gcc.dg/builtin-complex-err-1.c        (revision 0)
@@ -0,0 +1,26 @@
+/* Test __builtin_complex errors.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c1x -pedantic-errors" } */
+
+typedef double D;
+
+double d;
+
+_Complex double dc = __builtin_complex (1.0, (D) 0.0);
+
+_Complex double dc2 = __builtin_complex (d, 0.0); /* { dg-error "not constant" 
} */
+
+_Complex float fc = __builtin_complex (1.0f, 1); /* { dg-error "not of real 
binary floating-point type" } */
+
+_Complex float fc2 = __builtin_complex (1, 1.0f); /* { dg-error "not of real 
binary floating-point type" } */
+
+_Complex float fc3 = __builtin_complex (1.0f, 1.0); /* { dg-error "different 
types" } */
+
+void
+f (void)
+{
+  __builtin_complex (0.0); /* { dg-error "expected" } */
+  __builtin_complex (0.0, 0.0, 0.0); /* { dg-error "expected" } */
+}
+
+void (*p) (void) = __builtin_complex; /* { dg-error "expected" } */
Index: gcc/testsuite/gcc.dg/torture/builtin-complex-1.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/builtin-complex-1.c    (revision 0)
+++ gcc/testsuite/gcc.dg/torture/builtin-complex-1.c    (revision 0)
@@ -0,0 +1,117 @@
+/* Test __builtin_complex semantics.  */
+/* { dg-do run } */
+/* { dg-options "-std=c1x -pedantic-errors" } */
+
+extern void exit (int);
+extern void abort (void);
+
+#define COMPARE_BODY(A, B, TYPE, COPYSIGN)                             \
+  do {                                                                 \
+    TYPE s1 = COPYSIGN ((TYPE) 1.0, A);                                        
\
+    TYPE s2 = COPYSIGN ((TYPE) 1.0, B);                                        
\
+    if (s1 != s2)                                                      \
+      abort ();                                                                
\
+    if ((__builtin_isnan (A) != 0) != (__builtin_isnan (B) != 0))      \
+      abort ();                                                                
\
+    if ((A != B) != (__builtin_isnan (A) != 0))                                
\
+      abort ();                                                                
\
+  } while (0)
+
+void
+comparef (float a, float b)
+{
+  COMPARE_BODY (a, b, float, __builtin_copysignf);
+}
+
+void
+compare (double a, double b)
+{
+  COMPARE_BODY (a, b, double, __builtin_copysign);
+}
+
+void
+comparel (long double a, long double b)
+{
+  COMPARE_BODY (a, b, long double, __builtin_copysignl);
+}
+
+void
+comparecf (_Complex float a, float r, float i)
+{
+  comparef (__real__ a, r);
+  comparef (__imag__ a, i);
+}
+
+void
+comparec (_Complex double a, double r, double i)
+{
+  compare (__real__ a, r);
+  compare (__imag__ a, i);
+}
+
+void
+comparecl (_Complex long double a, long double r, long double i)
+{
+  comparel (__real__ a, r);
+  comparel (__imag__ a, i);
+}
+
+#define VERIFY(A, B, TYPE, COMPARE)                    \
+  do {                                                 \
+    TYPE a = A;                                                \
+    TYPE b = B;                                                \
+    _Complex TYPE cr = __builtin_complex (a, b);       \
+    static _Complex TYPE cs = __builtin_complex (A, B);        \
+    COMPARE (cr, A, B);                                        \
+    COMPARE (cs, A, B);                                        \
+  } while (0)
+
+#define ALL_CHECKS(PZ, NZ, NAN, INF, TYPE, COMPARE)    \
+  do {                                                 \
+    VERIFY (PZ, PZ, TYPE, COMPARE);                    \
+    VERIFY (PZ, NZ, TYPE, COMPARE);                    \
+    VERIFY (PZ, NAN, TYPE, COMPARE);                   \
+    VERIFY (PZ, INF, TYPE, COMPARE);                   \
+    VERIFY (NZ, PZ, TYPE, COMPARE);                    \
+    VERIFY (NZ, NZ, TYPE, COMPARE);                    \
+    VERIFY (NZ, NAN, TYPE, COMPARE);                   \
+    VERIFY (NZ, INF, TYPE, COMPARE);                   \
+    VERIFY (NAN, PZ, TYPE, COMPARE);                   \
+    VERIFY (NAN, NZ, TYPE, COMPARE);                   \
+    VERIFY (NAN, NAN, TYPE, COMPARE);                  \
+    VERIFY (NAN, INF, TYPE, COMPARE);                  \
+    VERIFY (INF, PZ, TYPE, COMPARE);                   \
+    VERIFY (INF, NZ, TYPE, COMPARE);                   \
+    VERIFY (INF, NAN, TYPE, COMPARE);                  \
+    VERIFY (INF, INF, TYPE, COMPARE);                  \
+  } while (0)
+
+void
+check_float (void)
+{
+  ALL_CHECKS (0.0f, -0.0f, __builtin_nanf(""), __builtin_inff(),
+             float, comparecf);
+}
+
+void
+check_double (void)
+{
+  ALL_CHECKS (0.0, -0.0, __builtin_nan(""), __builtin_inf(),
+             double, comparec);
+}
+
+void
+check_long_double (void)
+{
+  ALL_CHECKS (0.0l, -0.0l, __builtin_nanl(""), __builtin_infl(),
+             long double, comparecl);
+}
+
+int
+main (void)
+{
+  check_float ();
+  check_double ();
+  check_long_double ();
+  exit (0);
+}
Index: gcc/testsuite/gcc.dg/builtin-complex-err-2.c
===================================================================
--- gcc/testsuite/gcc.dg/builtin-complex-err-2.c        (revision 0)
+++ gcc/testsuite/gcc.dg/builtin-complex-err-2.c        (revision 0)
@@ -0,0 +1,10 @@
+/* Test __builtin_complex errors.  Verify it does nto allow quiet
+   creation of complex types in C90.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c90 -pedantic-errors" } */
+
+void
+f (void)
+{
+  __builtin_complex (0.0, 0.0); /* { dg-error "ISO C90 does not support 
complex types" } */
+}
Index: gcc/c-parser.c
===================================================================
--- gcc/c-parser.c      (revision 177894)
+++ gcc/c-parser.c      (working copy)
@@ -6023,6 +6023,7 @@ c_parser_alignof_expression (c_parser *p
                             assignment-expression ,
                             assignment-expression )
      __builtin_types_compatible_p ( type-name , type-name )
+     __builtin_complex ( assignment-expression , assignment-expression )
 
    offsetof-member-designator:
      identifier
@@ -6405,6 +6406,52 @@ c_parser_postfix_expression (c_parser *p
              = comptypes (e1, e2) ? integer_one_node : integer_zero_node;
          }
          break;
+       case RID_BUILTIN_COMPLEX:
+         c_parser_consume_token (parser);
+         if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+           {
+             expr.value = error_mark_node;
+             break;
+           }
+         loc = c_parser_peek_token (parser)->location;
+         e1 = c_parser_expr_no_commas (parser, NULL);
+         if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>"))
+           {
+             c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+             expr.value = error_mark_node;
+             break;
+           }
+         e2 = c_parser_expr_no_commas (parser, NULL);
+         c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+                                    "expected %<)%>");
+         mark_exp_read (e1.value);
+         mark_exp_read (e2.value);
+         if (!SCALAR_FLOAT_TYPE_P (TREE_TYPE (e1.value))
+             || DECIMAL_FLOAT_TYPE_P (TREE_TYPE (e1.value))
+             || !SCALAR_FLOAT_TYPE_P (TREE_TYPE (e2.value))
+             || DECIMAL_FLOAT_TYPE_P (TREE_TYPE (e2.value)))
+           {
+             error_at (loc, "%<__builtin_complex%> operand "
+                       "not of real binary floating-point type");
+             expr.value = error_mark_node;
+             break;
+           }
+         if (TYPE_MAIN_VARIANT (TREE_TYPE (e1.value))
+             != TYPE_MAIN_VARIANT (TREE_TYPE (e2.value)))
+           {
+             error_at (loc,
+                       "%<__builtin_complex%> operands of different types");
+             expr.value = error_mark_node;
+             break;
+           }
+         if (!flag_isoc99)
+           pedwarn (loc, OPT_pedantic,
+                    "ISO C90 does not support complex types");
+         expr.value = build2 (COMPLEX_EXPR,
+                              build_complex_type (TYPE_MAIN_VARIANT
+                                                  (TREE_TYPE (e1.value))),
+                              e1.value, e2.value);
+         break;
        case RID_AT_SELECTOR:
          gcc_assert (c_dialect_objc ());
          c_parser_consume_token (parser);

-- 
Joseph S. Myers
jos...@codesourcery.com

Reply via email to