-
Notifications
You must be signed in to change notification settings - Fork 248
Description
Summary
Several cuda.bindings Cython interfaces (cynvvm, cynvml, cynvjitlink, cynvfatbin) define types using Cython's quoted C name syntax without cdef extern from blocks. This causes C compilation errors when a user cimports these types in a module that also transitively includes the corresponding C header.
Problem
The externed interfaces (cynvrtc, cydriver, cyruntime) use cdef extern from "header.h" blocks, so Cython emits a #include directive and the header's include guards prevent duplicate definitions.
The non-externed interfaces instead define types inline:
# cynvvm.pxd (current)
ctypedef enum nvvmResult "nvvmResult":
NVVM_SUCCESS "NVVM_SUCCESS" = 0
NVVM_ERROR_OUT_OF_MEMORY "NVVM_ERROR_OUT_OF_MEMORY" = 1
...When Cython compiles a .pyx that cimports these types, it generates its own enum/typedef definitions in the C output. If any cdef extern from block in the same module (directly or via its .pxd) includes a header that transitively includes nvvm.h, the C compiler sees duplicate enum constant definitions and fails.
Minimal Reproducer
nvvm.h (or the real one from the CUDA Toolkit):
#ifndef NVVM_H
#define NVVM_H
typedef enum {
NVVM_SUCCESS = 0,
NVVM_ERROR_OUT_OF_MEMORY = 1,
NVVM_ERROR_PROGRAM_CREATION_FAILURE = 2,
NVVM_ERROR_INVALID_INPUT = 4,
NVVM_ERROR_INVALID_PROGRAM = 5,
} nvvmResult;
typedef void *nvvmProgram;
#endifexample.h — a user header that happens to transitively include nvvm.h:
#ifndef EXAMPLE_H
#define EXAMPLE_H
#include "nvvm.h"
int test_func(int a);
#endifexample.pxd — declares the user's C function:
cdef extern from "example.h":
int test_func(int a)example.pyx — cimports the nvvm types from cuda.bindings:
from cuda.bindings.cynvvm cimport nvvmResult
def call_test(int x):
return test_func(x)Build Output
example.c:1541:3: error: redeclaration of enumerator 'NVVM_SUCCESS'
1541 | NVVM_SUCCESS = 0,
| ^~~~~~~~~~~~
In file included from example.h:4,
from example.c:1138:
nvvm.h:5:5: note: previous definition of 'NVVM_SUCCESS' with type 'enum <anonymous>'
5 | NVVM_SUCCESS = 0,
| ^~~~~~~~~~~~
example.c:1542:3: error: redeclaration of enumerator 'NVVM_ERROR_OUT_OF_MEMORY'
...
example.c:1547:25: error: conflicting types for 'nvvmResult'; have 'enum nvvmResult'
The generated C file first emits #include "example.h" (which transitively includes nvvm.h), then later emits Cython's own enum nvvmResult { NVVM_SUCCESS = 0, ... } from the cimport. The duplicate enum constants are a hard C compilation error.
Impact
Any downstream Cython project that wants to:
- Use types from
cynvvm,cynvml,cynvjitlink, orcynvfatbinviacimport, and - Has any
cdef extern fromthat transitively includes the corresponding C header
...will fail to compile. The user doesn't need to redeclare any types themselves — the mere transitive include is sufficient.
This does not affect the externed interfaces (cynvrtc, cydriver, cyruntime) since they use cdef extern from, which emits #include directives that are deduplicated by include guards.
Affected Interfaces
| Interface | Style | Affected |
|---|---|---|
cynvvm.pxd |
Non-externed (quoted names) | Yes |
cynvml.pxd |
Non-externed (quoted names) | Yes |
cynvjitlink.pxd |
Non-externed (quoted names) | Yes |
cynvfatbin.pxd |
Non-externed (quoted names) | Yes |
cynvrtc.pxd |
cdef extern from "nvrtc.h" |
No |
cydriver.pxd |
cdef extern from "cuda.h" |
No |
cyruntime.pxd |
cdef extern from "driver_types.h" |
No |
Suggested Fix
Switch the non-externed interfaces to use cdef extern from blocks, consistent with cynvrtc.pxd, cydriver.pxd, and cyruntime.pxd. This causes Cython to emit #include directives instead of generating its own type definitions, allowing include guards to deduplicate.
Edit: On further analysis, this is not believed to be an ABI break. The .pxd file is purely a compile-time artifact — it is not referenced at runtime by already-compiled extensions. Since the quoted C names already produce the exact same C type identifiers and enum constant values as the header, the generated machine code is identical regardless of declaration style. Already-compiled downstream extensions would continue to work without recompilation against an updated cuda.bindings that makes this change.