Skip to content

Commit aded4fa

Browse files
rparolinclaude
andcommitted
fix(cuda.core): reject double-dashed NVVM options at the source
Add a guard at the single exit point of _prepare_nvvm_options_impl that raises if any option is double-dashed. libNVVM accepts only single-dashed options, and every option on this path is generated from typed fields, so a double dash can only mean a bug in cuda.core rather than bad user input. Raising here names the offending option instead of leaving the user with libNVVM's opaque NVVM_ERROR_INVALID_OPTION. The guard is a separate cpdef helper so a test can exercise it directly; an inline check would be unreachable and therefore unverifiable. A second test sets every NVVM-supported field of ProgramOptions and asserts no emitted option is double-dashed, so the invariant covers options added later. Verified by mutation: restoring --numba-debug turns three tests red, and the compile test then fails with this guard's error rather than libNVVM's. The NVRTC path keeps --numba-debug, which it accepts; the guard is scoped to the NVVM emitter. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 42c66a8 commit aded4fa

3 files changed

Lines changed: 65 additions & 0 deletions

File tree

‎cuda_core/cuda/core/_program.pyi‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,17 @@ _nvvm_import_attempted = False
429429
def _can_load_generated_ptx() -> bool:
430430
"""Check if the driver can load PTX generated by the current NVRTC version."""
431431

432+
def _assert_single_dashed_nvvm_options(options: list[str]) -> None:
433+
"""Guard against emitting a double-dashed option to libNVVM.
434+
435+
libNVVM's parser accepts only single-dashed options and rejects the
436+
double-dashed spelling of every option with NVVM_ERROR_INVALID_OPTION
437+
(see #2570). Every option on this path is generated from typed fields, so
438+
a double dash can only mean a bug in ``cuda.core`` rather than bad user
439+
input. Fail here, naming the option, instead of leaving the user with
440+
libNVVM's opaque error.
441+
"""
442+
432443
def _program_compile_uncached(program, target_type, name_expressions, logs):
433444
"""Run ``Program_compile`` without the cache wrapper.
434445

‎cuda_core/cuda/core/_program.pyx‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,24 @@ cdef inline list _prepare_nvrtc_options_impl(object opts):
12251225
return [o.encode() for o in options]
12261226

12271227

1228+
cpdef void _assert_single_dashed_nvvm_options(options: list[str]) except *:
1229+
"""Guard against emitting a double-dashed option to libNVVM.
1230+
1231+
libNVVM's parser accepts only single-dashed options and rejects the
1232+
double-dashed spelling of every option with NVVM_ERROR_INVALID_OPTION
1233+
(see #2570). Every option on this path is generated from typed fields, so
1234+
a double dash can only mean a bug in ``cuda.core`` rather than bad user
1235+
input. Fail here, naming the option, instead of leaving the user with
1236+
libNVVM's opaque error.
1237+
"""
1238+
for option in options:
1239+
if option.startswith("--"):
1240+
raise RuntimeError(
1241+
f"Internal error: NVVM option {option!r} is double-dashed. libNVVM accepts "
1242+
f"only single-dashed options; emit {option[1:]!r} instead."
1243+
)
1244+
1245+
12281246
cdef inline object _prepare_nvvm_options_impl(object opts, bint as_bytes):
12291247
"""Build NVVM-specific compiler options."""
12301248
options = []
@@ -1320,6 +1338,8 @@ cdef inline object _prepare_nvvm_options_impl(object opts, bint as_bytes):
13201338
if unsupported:
13211339
raise CUDAError(f"The following options are not supported by NVVM backend: {', '.join(unsupported)}")
13221340

1341+
_assert_single_dashed_nvvm_options(options)
1342+
13231343
if as_bytes:
13241344
return [o.encode() for o in options]
13251345
else:

‎cuda_core/tests/test_program.py‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,40 @@ def test_nvvm_program_options_as_bytes_numba_debug():
775775
assert b"-g" in nvvm_bytes
776776

777777

778+
@pytest.mark.agent_authored(model="claude-opus-5[1m]")
779+
def test_nvvm_options_reject_double_dash():
780+
"""The guard must name a double-dashed option rather than let libNVVM
781+
reject it with an opaque error (see #2570)."""
782+
from cuda.core._program import _assert_single_dashed_nvvm_options
783+
784+
_assert_single_dashed_nvvm_options(["-arch=compute_80", "-g", "-numba-debug"])
785+
786+
with pytest.raises(RuntimeError, match=r"--numba-debug.*double-dashed"):
787+
_assert_single_dashed_nvvm_options(["-arch=compute_80", "--numba-debug"])
788+
789+
790+
@nvvm_available
791+
@pytest.mark.agent_authored(model="claude-opus-5[1m]")
792+
def test_nvvm_program_options_as_bytes_all_single_dashed():
793+
"""Every option cuda.core emits to libNVVM must be single-dashed, because
794+
libNVVM rejects the double-dashed spelling of all of them (see #2570).
795+
This covers every NVVM-supported field of ProgramOptions."""
796+
options = ProgramOptions(
797+
arch="sm_80",
798+
debug=True,
799+
numba_debug=True,
800+
device_code_optimize=True,
801+
ftz=True,
802+
prec_sqrt=True,
803+
prec_div=True,
804+
fma=True,
805+
)
806+
nvvm_bytes = options.as_bytes("nvvm")
807+
assert nvvm_bytes, "expected at least one emitted option"
808+
offenders = [o for o in nvvm_bytes if o.startswith(b"--")]
809+
assert not offenders, f"double-dashed options are rejected by libNVVM: {offenders}"
810+
811+
778812
@nvvm_available
779813
@pytest.mark.skipif(
780814
not _check_nvvm_supports_numba_debug(),

0 commit comments

Comments
 (0)