Skip to content

[C99] Claim conformance for _Complex support #88161

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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, "");
Comment on lines +54 to +55
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is using an extension for complex initializers, not a standard feature, so I'm a little bit uncomfortable that it's actually testing the assertion that the first element of the array corresponds to the real.

That said, I'm not sure there's any non-extension way to test that as a constant expression anyways, so... 🤷

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other alternative would be to use a builtin function, I can go either way. Maybe I should use one of each to show they're equivalent?


_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