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