Skip to content

Commit

Permalink
[C99] Claim conformance for _Complex support (#88161)
Browse files Browse the repository at this point in the history
There's so much overlap between the cited papers so this condenses the
status page into a single entry rather than trying to test conformance
against multiple papers doing conflicting things.
  • Loading branch information
AaronBallman committed Apr 12, 2024
1 parent 83dc419 commit 0318ce8
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 22 deletions.
122 changes: 122 additions & 0 deletions clang/test/C/C99/n809.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// RUN: %clang_cc1 -verify -std=c99 %s

/* WG14 N620, N638, N657, N694, N809: Partial
* Complex and imaginary support in <complex.h>
*
* NB: Clang supports _Complex but not _Imaginary. In C99, _Complex support is
* required outside of freestanding, but _Imaginary support is fully optional.
* In C11, both are made fully optional.
*
* NB: _Complex support requires an underlying support library such as
* compiler-rt to provide functions like __divsc3. Compiler-rt is not supported
* on Windows.
*
* Because the functionality is so intertwined between the various papers,
* we're testing all of the functionality in one file.
*/

// Demonstrate that we support spelling complex floating-point objects.
float _Complex f1;
_Complex float f2;

double _Complex d1;
_Complex double d2;

long double _Complex ld1;
_Complex long double ld2;

// Show that we don't support spelling imaginary types.
float _Imaginary fi1; // expected-error {{imaginary types are not supported}}
_Imaginary float fi2; // expected-error {{imaginary types are not supported}}

double _Imaginary di1; // expected-error {{imaginary types are not supported}}
_Imaginary double di2; // expected-error {{imaginary types are not supported}}

long double _Imaginary ldi1; // expected-error {{imaginary types are not supported}}
_Imaginary long double ldi2; // expected-error {{imaginary types are not supported}}

// Each complex type has the same representation and alignment as an array
// containing two elements of the corresponding real type. Note, it is not
// mandatory that the alignment of a structure containing an array of two
// elements has the same alignment as an array of two elements outside of a
// structure, but this is a property Clang supports.
_Static_assert(sizeof(float _Complex) == sizeof(struct { float mem[2]; }), "");
_Static_assert(_Alignof(float _Complex) == _Alignof(struct { float mem[2]; }), "");

_Static_assert(sizeof(double _Complex) == sizeof(struct { double mem[2]; }), "");
_Static_assert(_Alignof(double _Complex) == _Alignof(struct { double mem[2]; }), "");

_Static_assert(sizeof(long double _Complex) == sizeof(struct { long double mem[2]; }), "");
_Static_assert(_Alignof(long double _Complex) == _Alignof(struct { long double mem[2]; }), "");

// The first element corresponds to the real part and the second element
// corresponds to the imaginary part.
_Static_assert(__real((float _Complex){ 1.0f, 2.0f }) == 1.0f, "");
_Static_assert(__imag((float _Complex){ 1.0f, 2.0f }) == 2.0f, "");

_Static_assert(__real((double _Complex){ 1.0, 2.0 }) == 1.0, "");
_Static_assert(__imag((double _Complex){ 1.0, 2.0 }) == 2.0, "");

_Static_assert(__real((long double _Complex){ 1.0L, 2.0L }) == 1.0L, "");
_Static_assert(__imag((long double _Complex){ 1.0L, 2.0L }) == 2.0L, "");

// When a real value is converted to a complex value, the real part follows the
// usual conversion rules and the imaginary part should be zero.
_Static_assert(__real((float _Complex)1.0f) == 1.0f, "");
_Static_assert(__imag((float _Complex)1.0f) == 0.0f, "");

_Static_assert(__real((double _Complex)1.0f) == 1.0, "");
_Static_assert(__imag((double _Complex)1.0f) == 0.0, "");

_Static_assert(__real((long double _Complex)1.0f) == 1.0L, "");
_Static_assert(__imag((long double _Complex)1.0f) == 0.0L, "");

// When a complex value is converted to a real value, the real part follows the
// usual conversion rules and the imaginary part is discarded.
_Static_assert((float)(float _Complex){ 1.0f, 2.0f } == 1.0f, "");
_Static_assert((double)(float _Complex){ 1.0f, 2.0f } == 1.0, "");
_Static_assert((long double)(float _Complex){ 1.0f, 2.0f } == 1.0L, "");

// Complex values are only equal if both the real and imaginary parts are equal.
_Static_assert((float _Complex){ 1.0f, 2.0f } == (float _Complex){ 1.0f, 2.0f }, "");
_Static_assert((double _Complex){ 1.0, 2.0 } == (double _Complex){ 1.0, 2.0 }, "");
_Static_assert((long double _Complex){ 1.0L, 2.0L } == (long double _Complex){ 1.0L, 2.0L }, "");

_Static_assert((float _Complex){ 1.0f, 2.0f } != (float _Complex){ 2.0f, 0.0f }, "");
_Static_assert((double _Complex){ 1.0, 2.0 } != (double _Complex){ 2.0, 0.0 }, "");
_Static_assert((long double _Complex){ 1.0L, 2.0L } != (long double _Complex){ 2.0L, 0.0L }, "");

// You cannot use relational operator on complex values.
int i1 = (float _Complex){ 1.0f, 2.0f } < 10; // expected-error {{invalid operands to binary expression}}
int i2 = (double _Complex){ 1.0f, 2.0f } > 10; // expected-error {{invalid operands to binary expression}}
int i3 = (long double _Complex){ 1.0f, 2.0f } <= 10; // expected-error {{invalid operands to binary expression}}
int i4 = (float _Complex){ 1.0f, 2.0f } >= 10; // expected-error {{invalid operands to binary expression}}

// As a type specifier, _Complex cannot appear alone; however, we support it as
// an extension by assuming _Complex double.
_Complex c = 1.0f; // expected-warning {{plain '_Complex' requires a type specifier; assuming '_Complex double'}}
// Because we don't support imaginary types, we don't extend the extension to
// that type specifier.
// FIXME: the warning diagnostic here is incorrect and should not be emitted.
_Imaginary i = 1.0f; // expected-warning {{plain '_Complex' requires a type specifier; assuming '_Complex double'}} \
expected-error {{imaginary types are not supported}}

void func(void) {
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wpedantic"
// Increment and decrement operators have a constraint that their operand be
// a real type; Clang supports this as an extension on complex types as well.
_Complex float cf = 0.0f;

cf++; // expected-warning {{'++' on an object of complex type is a Clang extension}}
++cf; // expected-warning {{'++' on an object of complex type is a Clang extension}}

cf--; // expected-warning {{'--' on an object of complex type is a Clang extension}}
--cf; // expected-warning {{'--' on an object of complex type is a Clang extension}}

// However, unary + and - are fine, as is += 1.
(void)-cf;
(void)+cf;
cf += 1;
#pragma clang diagnostic pop
}
64 changes: 64 additions & 0 deletions clang/test/C/C99/n809_2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// RUN: %clang_cc1 -ast-dump -std=c99 %s | FileCheck %s

void variadic(int i, ...);

void func(void) {
// CHECK: FunctionDecl {{.*}} func 'void (void)'

// Show that we correctly convert between two complex domains.
_Complex float cf = 1.0f;
_Complex double cd;

cd = cf;
// CHECK: BinaryOperator {{.*}} '_Complex double' '='
// CHECK-NEXT: DeclRefExpr {{.*}} 'cd'
// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Complex double' <FloatingComplexCast>
// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Complex float' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr {{.*}} 'cf'

cf = cd;
// CHECK: BinaryOperator {{.*}} '_Complex float' '='
// CHECK-NEXT: DeclRefExpr {{.*}} 'cf'
// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Complex float' <FloatingComplexCast>
// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Complex double' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr {{.*}} 'cd'

// Show that we correctly convert to the common type of a complex and real.
// This should convert the _Complex float to a _Complex double ("without
// change of domain" c.f. C99 6.3.1.8p1).
(void)(cf + 1.0);
// CHECK: BinaryOperator {{.*}} '_Complex double' '+'
// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Complex double' <FloatingComplexCast>
// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Complex float' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr {{.*}} 'cf'
// CHECK-NEXT: FloatingLiteral {{.*}} 'double' 1.0

// This should convert the float constant to double, then produce a
// _Complex double.
(void)(cd + 1.0f);
// CHECK: BinaryOperator {{.*}} '_Complex double' '+'
// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Complex double' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr {{.*}} 'cd'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast>
// CHECK-NEXT: FloatingLiteral {{.*}} 'float' 1.0

// This should convert the int constant to float, then produce a
// _Complex float.
(void)(cf + 1);
// CHECK: BinaryOperator {{.*}} '_Complex float' '+'
// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Complex float' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr {{.*}} 'cf'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <IntegralToFloating>
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1

// Show that we do not promote a _Complex float to _Complex double as part of
// the default argument promotions when passing to a variadic function.
variadic(1, cf);
// CHECK: CallExpr
// CHECK-NEXT: ImplicitCastExpr {{.*}} <FunctionToPointerDecay>
// CHECK-NEXT: DeclRefExpr {{.*}} 'variadic'
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1
// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Complex float' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr {{.*}} 'cf'
}

12 changes: 12 additions & 0 deletions clang/test/C/C99/n809_3.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// RUN: %clang_cc1 -emit-llvm -std=c99 %s -o - | FileCheck %s

// Demonstrate that statics are properly zero initialized.
static _Complex float f_global;
void func(void) {
static _Complex double d_local;
d_local = f_global;
}

// CHECK-DAG: @func.d_local = internal global { double, double } zeroinitializer
// CHECK-DAG: @f_global = internal global { float, float } zeroinitializer

37 changes: 15 additions & 22 deletions clang/www/c_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -132,29 +132,22 @@ <h2 id="c99">C99 implementation status</h2>
<td>Unknown</td>
<td class="full" align="center">Yes</td>
</tr>
<tr id="complex">
<td rowspan="6">complex and imaginary support in &lt;complex.h&gt;</td>
<tr>
<td>complex and imaginary support in &lt;complex.h&gt;</td>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n693.ps">N693</a></td>
<td class="partial" align="center">
<details><summary>Partial</summary>
Clang supports <code>_Complex</code> type specifiers but does not
support <code>_Imaginary</code> type specifiers. Support for
<code>_Imaginary</code> is optional in C99 and Clang does not claim
conformance to Annex G.<br />
<br />
<code>_Complex</code> support requires an underlying support library
such as compiler-rt to provide functions like <code>__divsc3</code>,
but compiler-rt is not supported on Windows.
</details>
</td>
</tr>
<tr>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n620.ps">N620</a></td>
<td class="unknown" align="center">Unknown</td>
</tr>
<tr>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n638.ps">N638</a></td>
<td class="unknown" align="center">Unknown</td>
</tr>
<tr>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n657.ps">N657</a></td>
<td class="unknown" align="center">Unknown</td>
</tr>
<tr>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n694.ps">N694</a></td>
<td class="unknown" align="center">Unknown</td>
</tr>
<tr>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n809.ps">N809</a></td>
<td class="unknown" align="center">Unknown</td>
</tr>
<tr>
<td>type-generic math macros in &lt;tgmath.h&gt;</td>
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n693.ps">N693</a></td>
Expand Down

0 comments on commit 0318ce8

Please sign in to comment.