Skip to content

BUG: npy_math.h complex accessors call UCRT creal/cimag, but npy_cdouble is C99 _Complex for icx on Windows #32482

Open

Description

@vchamarthi

Describe the issue:

Since gh-31389 (fix for gh-31337), npy_common.h excludes __INTEL_LLVM_COMPILER from
the _MSC_VER branch, so for icx on Windows npy_cdouble is double _Complex rather
than _Dcomplex:

#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__INTEL_LLVM_COMPILER)
    typedef _Dcomplex npy_cdouble;
#else
    typedef double _Complex npy_cdouble;

But npy_math.h's accessors still call the UCRT functions in the C path:

static inline double npy_creal(const npy_cdouble z)
{
#if defined(__cplusplus)
    return z._Val[0];
#else
    return creal(z);
#endif
}

Windows UCRT declares creal(_Dcomplex), crealf(_Fcomplex), creall(_Lcomplex)
regardless of which frontend compiles it, since it is the same CRT. So the two
headers disagree and including <numpy/npy_math.h> alone fails to compile.

Note the __cplusplus path already avoids the UCRT call by using z._Val[0]; only the
C path is affected.

Suggested fix: use real/imag (or z._Val[0]) instead of UCRT creal/cimag when
_MSC_VER is defined, or do not switch the typedefs based on the CRT at all.

Compiling with -U__INTEL_LLVM_COMPILER makes it build again, which confirms the
typedef selection is the trigger.

This is a different failure location than gh-31337 - that one was numpy's own
meson.build _Fcomplex type check; this one is npy_math.h and affects downstream
projects using the installed headers.

Affects numpy >= 2.4.5 and all 2.5.x.

Reproduce the code example:

// repro.c
#include <Python.h>
#include <numpy/npy_math.h>


icx /c repro.c -I"%PREFIX%\Lib\site-packages\numpy\_core\include" -I"%PREFIX%\Include"

Error message:

npy_math.h(368,18): error: passing 'const npy_cdouble' (aka 'const _Complex double') to parameter of incompatible type '_Dcomplex' (aka 'struct _C_double_complex')
  368 |     return creal(z);
      |                  ^
ucrt\complex.h(89,49): note: passing argument to parameter '_Z' here
   89 | _ACRTIMP double    __cdecl creal(_In_ _Dcomplex _Z);
      |                                                 ^

6 errors generated.

Python and NumPy Versions:

numpy 2.5.2
Python 3.13.15

Runtime Environment:

Windows x86_64
Intel oneAPI DPC++/C++ Compiler (icx) 2026.2.0
Windows SDK / UCRT 10.0.26100.0

How does this issue affect you or how did you find it:

It breaks Windows builds of mkl_umath (https://github.com/IntelPython/mkl_umath),
which compiles with icx against the installed numpy headers. Bisected by compiling
the reproducer against each release: 2.4.4 builds, 2.4.5 fails. The 2.4.4 headers
have no __INTEL_LLVM_COMPILER reference at all; 2.4.5 introduces it.

Activity

  1. rgommers commented on Sep 3, 2026

    @rgommers
    Member

    Thanks for the report @vchamarthi. I see the problem - the current treatment in the header cannot be correct for both of:

    • Compile NumPy with MSVC, then a package depending on it with Intel compilers
    • Compile both NumPy and a package depending on it with Intel compilers

    Suggested fix: use real/imag (or z._Val[0]) instead of UCRT creal/cimag when _MSC_VER is defined, or do not switch the typedefs based on the CRT at all.

    I'll have a closer look, but I think the latter approach is preferred.

  2. rgommers commented on Sep 3, 2026

    @rgommers
    Member

    This complex type handling is still a mess, with multiple related issues. There's the immediate compile error above, there's a NumPy-internal C/C++ mix mess, and there's an ABI part (xref gh-20880 yet again).

    I did an audit with Claude, and there are a number of inconsistencies in how complex types are defined (line numbers as of commit fc5a53a):

    file:line condition what icx-cl gets
    numpy/_core/include/numpy/npy_common.h:395 _MSC_VER && !__INTEL_COMPILER && !__INTEL_LLVM_COMPILER double _Complex
    numpy/_core/src/npymath/npy_math_complex.c.src:68 _MSC_VER && !__INTEL_COMPILER {1.0, 0.0} brace init
    numpy/_core/feature_detection_cmath.h:11 _MSC_VER && !__INTEL_COMPILER _Complex typedefs
    numpy/_core/meson.build:183 cc.get_id() in {msvc, clang-cl} C99 sizeof branch

    So NumPy built with icx is already internally inconsistent today: it applies a struct brace-initializer to a _Complex typedef, and only compiles because clang accepts that as an extension.

    I think we need a small fix that's back-portable (restoring the 2.4.4 behavior in npy_common.h), and a larger fix to clean up more of this, getting rid of all these inconsistent defines. The meson.build part of gh-31389 should be kept it looks like.

  3. added this to the 2.6.0 Release milestone on Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions