Fix nvcc CUDA 12.0/12.1 C++20 ranges parse error (#3907)#5248
Merged
Conversation
The ci_cuda_example job compiled against the json-ci image's CUDA 11.0 toolkit at cuda_std_11, which cannot exercise #3907 (a c++20 parse error in iteration_proxy.hpp's enable_borrowed_range reported under nvcc). Switch the job to pull official nvidia/cuda devel images directly and matrix across CUDA 11.8-12.6 at cuda_std_20 so CI can empirically confirm which versions are actually affected before any source-level fix is attempted. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
nlohmann
marked this pull request as draft
July 8, 2026 19:01
The diagnostic matrix in this PR confirmed the affected range exactly: nvcc 12.0.1 and 12.1.1 both fail with "expected initializer before '<' token" on iteration_proxy.hpp's enable_borrowed_range variable template specialization at -std=c++20; 12.2.2 and newer already build cleanly. Guard JSON_HAS_RANGES off for that narrow nvcc version range, matching the existing GCC-11/libstdc++ carve-outs in the same ifdef chain, and regenerate single_include accordingly. Broaden the CUDA smoke test to also exercise comparisons (operator==/operator<=>, gated independently by JSON_HAS_THREE_WAY_COMPARISON) and range-based iteration, not just dump()/erase(), so the fix's actual scope is evidenced by CI rather than assumed from the single reported symptom. Have tests/cuda_example/CMakeLists.txt pick the newest C++ standard the detected nvcc version actually supports (20/17/11) instead of hard-requiring C++20, so older toolkits build at a lower standard instead of failing CMake configure outright. This is test-project-local only; the JSON_HAS_RANGES guard is what protects real client code, since a header can't control what -std= flag it's compiled with. Right-size the CI matrix from the 8-version diagnostic sweep down to 11.8.0 (C++17 fallback path) / 12.1.1 (permanent #3907 regression guard) / 12.6.3 (recent coverage), and update the compiler-version table in the quality assurance docs to match. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
The 11.8.0 leg's graceful C++17 fallback (added in the previous commit) worked correctly, but the broadened smoke test used the <=> operator unconditionally, which isn't valid syntax pre-C++20 — nvcc rejected it with "expected an expression" once the CMake logic picked cuda_std_17 for the older toolkit. Gate those two lines behind JSON_HAS_THREE_WAY_COMPARISON like the library itself does internally. Sanity-compiled the file as plain C++ at both -std=c++17 (skips the guarded block) and -std=c++20 (includes it) locally; the actual nvcc build is verified via CI on PR #5248. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
nlohmann
marked this pull request as ready for review
July 9, 2026 11:45
2 tasks
2 tasks
nlohmann
added a commit
that referenced
this pull request
Jul 9, 2026
PR #5248 added a 5th JSON_HAS_RANGES exclusion branch to macro_scope.hpp (nvcc CUDA 12.0.x/12.1.x, fixed in 12.2, issue #3907) shortly after #5252 added the "Known compiler/stdlib exclusions" list to json_has_ranges.md, so the new branch was missing from the just-added doc section. Bring the list back to parity with the code (5 exclusion branches, 5 documented). Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
make amalgamate.What and why
#3907 reports that
nlohmann/json.hppfails to compile undernvccat-std=c++20(
error: expected initializer before '<' tokeniniteration_proxy.hpp'senable_borrowed_rangespecialization), but compiles fine at C++17. The issue waspreviously closed as "un-reproducible in CI": the existing
ci_cuda_examplejob builtagainst the
json-ciimage's CUDA 11.0 toolkit atcuda_std_11, and CUDA 11 has noC++20 support at all, so the job could never hit this code path. A separate job
(
ci_nvhpc) and a previously closed PR (#4796) both use CUDA 12.9, a version where thebug is already fixed — so neither of those could catch it either.
This PR was done in two stages, both on this branch:
1. Diagnose first. Turned
ci_cuda_exampleinto a matrix pulling officialnvidia/cuda:*-devel-ubuntu22.04images directly (same pattern asci_nvhpc) across 8CUDA versions (11.8.0 through 12.6.3) at
cuda_std_20, to empirically confirm whichversions are actually affected instead of trusting an unverified issue-thread comment.
Result, reproduced against real nvcc:
So the affected range is exactly nvcc 12.0.x/12.1.x, fixed by NVIDIA from 12.2 onward —
nothing broader or narrower.
2. Fix it. Added a targeted guard to the
JSON_HAS_RANGESdetection chain ininclude/nlohmann/detail/macro_scope.hpp, disabling ranges support specifically fornvcc 12.0/12.1 (detected via
__CUDACC_VER_MAJOR__/__CUDACC_VER_MINOR__), matching thestyle of the neighboring GCC-11/libstdc++ carve-outs already in that file. This is the
part that protects real client code — any project compiling this header with nvcc
12.0/12.1 at C++20 gets
JSON_HAS_RANGESdisabled automatically, regardless of their ownbuild system. Regenerated
single_include/nlohmann/json.hppaccordingly.Also broadened
tests/cuda_example/json_cuda.cu(previously only calleddump()/erase()) to exerciseoperator==/operator<=>(three-way comparison, gatedindependently by
JSON_HAS_THREE_WAY_COMPARISON) and range-based iteration, so the CImatrix gives real evidence about the fix's scope rather than a guess. This did catch a
real issue: once
11.8.0started building at C++17 (see below), its use of<=>unconditionally broke, since that operator isn't valid pre-C++20 syntax — fixed by
gating those two lines behind
JSON_HAS_THREE_WAY_COMPARISON, the same way the libraryitself does internally.
tests/cuda_example/CMakeLists.txtno longer hard-requirescuda_std_20(which failedoutright on anything older than CUDA 12.0). It now picks the newest C++ standard the
detected nvcc version actually supports — 20 for CUDA ≥ 12.0, 17 for CUDA ≥ 11.0, else
11 — so older toolkits still build the smoke test at whatever standard they support
instead of erroring. This is local to our own test project only (a header can't control
its own
-std=invocation); the actual protection for downstream users is themacro_scope.hppguard above.With the fix in place, every CUDA version can build successfully at its own standard, so
the matrix no longer needs the full 8-version diagnostic sweep to avoid a permanently red
leg. Right-sized
ci_cuda_exampledown to 3 legs, each testing something distinct:11.8.0— exercises the new C++17 fallback path end-to-end12.1.1— the permanent regression guard for error: expected initializer before ‘<’ token #390712.6.3— recent CUDA/C++20 coverageAlso updated the CUDA row in
docs/mkdocs/docs/community/quality_assurance.md's compilertable to match (previously listed a stale
CUDA 11.0.221/ Ubuntu 20.04 entry).Final CI on this branch: all three
ci_cuda_examplelegs (11.8.0,12.1.1,12.6.3)pass.
Changes
include/nlohmann/detail/macro_scope.hpp/single_include/nlohmann/json.hpp: nvcc12.0/12.1
JSON_HAS_RANGESguard (the actual fix for error: expected initializer before ‘<’ token #3907).tests/cuda_example/json_cuda.cu: broadened smoke test (comparisons + range-basediteration),
<=>usage gated byJSON_HAS_THREE_WAY_COMPARISON.tests/cuda_example/CMakeLists.txt: graceful per-nvcc-version C++ standard selectioninstead of hard-requiring
cuda_std_20.cmake/ci.cmake: dropped the stale-DCMAKE_CUDA_HOST_COMPILER=g++-8pin on theci_cuda_exampletarget so it falls back to the stock host compiler in each image..github/workflows/ubuntu.yml:ci_cuda_examplenow matrices overnvidia/cuda:{11.8.0,12.1.1,12.6.3}-devel-ubuntu22.04directly instead of the pinnedghcr.io/nlohmann/json-ciimage, with alukka/get-cmakestep added.docs/mkdocs/docs/community/quality_assurance.md: updated the CUDA compiler-versiontable row(s) to match.