https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121446
Bug ID: 121446 Summary: [OpenMP][OpenACC] 'atomic' directive rejects complex variables in C/C++ Product: gcc Version: 16.0 Status: UNCONFIRMED Keywords: openacc, openmp, rejects-valid Severity: normal Priority: P3 Component: c Assignee: unassigned at gcc dot gnu.org Reporter: burnus at gcc dot gnu.org CC: jakub at gcc dot gnu.org, tschwinge at gcc dot gnu.org Target Milestone: --- Found when looking at PR121416. That's about _Complex double _Complex int type variable in atomics. In Fortran, those are accepted and quite explicitly so following the spec. For OpenMP: The question is whether both or at least the first is valid for C (and C++?) At least clang-20 accepts both. For OpenACC: The wording is slightly odd, but implies that at least for C, some _Complex types are valid. Currently, gcc/g++ reject both as follows (with -fopenmp and -fopenacc): error: invalid expression type for ‘#pragma omp atomic’ [For completeness: Likewise with vector types.] * * * OpenMP requires: "* x, r (result), and v (as applicable) are lvalue expressions with scalar type." - with - "For C/C++, a scalar-variable, as defined by the base language." C23 defines it as: "Arithmetic types, pointer types, and the nullptr_t type are collectively called scalar types." - and - "Integer and floating types are collectively called arithmetic types. Each arithmetic type belongs to one type domain: the real type domain comprises the real types, the complex type domain comprises the complex types." C++23: "Arithmetic types (6.8.2), enumeration types, pointer types, pointer-to-member types (6.8.4), std::nullptr_t, and cv-qualified (6.8.5) versions of these types are collectively called scalar types." "Integral and floating-point types are collectively termed arithmetic types." * * * OpenACC requires: "* x and v (as applicable) are both l-value expressions with scalar type." - with - "In C, scalar datatypes are char (signed or unsigned), int (signed or unsigned, with optional short, long or long long attribute), enum, float, double, long double, Complex (with optional float or long attribute), or any pointer datatype. In C++, scalar datatypes are char (signed or unsigned), wchar t, int (signed or unsigned, with optional short, long or long long attribute), enum, bool, float, double, long double, or any pointer datatype." * * * Remarks: For OpenACC, I wonder why for C only with 'float', 'double' and 'long' and no '_Complex int' or '_Complex long double'. For C, I noticed that the standard only talks about _Complex + float/double/long double and not integral types. For C++, the standard does not have _Complex any only provides 'complex' via the header <complex> that defines a class template * * * In most hardware, I assume that complex float and int (intN_t, N <= 4?) should be supported, for complex double/long double + complex long/long long/intN_t (N >= 8) it might be more difficult. For more complex reductions (including involving 'complex') and for 'complex(8)', GCC uses GOMP_start/GOMP_stop (i.e. locking) – which looks similar to the libatomic fallback version, except that start/stop permits more code than just, e.g., a atomic_compare_exchange_16. [It seems as if in some cases, GOMP_atomic_start/GOMP_atomic_stop is used even though the data type is small enough, but I might have missed something.] * * * Test case * * * _Complex int i, j; _Complex double d, e; void f() { #pragma acc atomic update #pragma omp atomic update i += 1; #pragma acc atomic update #pragma omp atomic update i += d; #pragma acc atomic update #pragma omp atomic update d += e; } * * * A patch would be the following. – Note, however, the comment above, which had to be changed if accepted: --- a/gcc/c-family/c-omp.cc +++ b/gcc/c-family/c-omp.cc @@ -237,12 +237,15 @@ c_finish_omp_atomic (location_t loc, enum tree_code code, /* ??? According to one reading of the OpenMP spec, complex type are supported, but there are no atomic stores for any architecture. But at least icc 9.0 doesn't support complex types here either. And lets not even talk about vector types... */ type = TREE_TYPE (lhs); if (!INTEGRAL_TYPE_P (type) && !POINTER_TYPE_P (type) - && !SCALAR_FLOAT_TYPE_P (type)) + && !SCALAR_FLOAT_TYPE_P (type) + && !COMPLEX_FLOAT_TYPE_P (type) + && !(TREE_CODE (type) == COMPLEX_TYPE + && INTEGRAL_TYPE_P (TREE_TYPE (type)))) { error_at (loc, "invalid expression type for %<#pragma omp atomic%>"); return error_mark_node; }