This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git
The following commit(s) were added to refs/heads/main by this push:
new ff7e3d4f [FIX] Select the C++ standard for the torch addon by torch
version (#687)
ff7e3d4f is described below
commit ff7e3d4f6ece3ffcf48b7111932fd7b6bd77db5d
Author: Yaxing Cai <[email protected]>
AuthorDate: Mon Jul 27 06:04:35 2026 +0800
[FIX] Select the C++ standard for the torch addon by torch version (#687)
Torch 2.13 uses C++20 constructs in its public headers, such as
designated initializers and default member initializers for bit-fields,
and builds its own extensions with C++20 accordingly. The torch C-DLPack
addon still compiled with C++17, which makes the Windows wheel job fail
to compile:
```
error C7582: default member initializers for bit-fields requires at least
/std:c++20
error C7555: use of designated initializers requires at least /std:c++20
error C2139: 'torch::autograd::Node': an undefined class is not allowed as
an argument to compiler intrinsic type trait '__is_base_of'
```
GCC and Clang accept those constructs in C++17 mode as extensions and
only warn about them, so Linux and macOS keep building while MSVC
rejects them outright. That is why `main` has been red on Windows since
torch 2.13.0 (released 2026-07-08) while the other platforms stayed
green on the same commit.
### Changes
- Select the C++ standard from the installed torch version rather than
hard-coding it: C++20 for torch 2.13 and later, C++17 before that.
Applied to both the Windows (`/std:`) and Linux/macOS (`-std=`) build
paths.
- The build script already reports compiler errors through its own
stderr, but the tests discarded the child output and asserted on the
exit code alone, so failures surfaced as a bare `exit status 1` and hid
the diagnostics above. Capture that output and attach it to the
assertion.
### Testing
- `tests/python/test_optional_torch_c_dlpack.py` passes on Linux with
torch 2.13.0, building the addon from a clean cache.
- Version selection verified across torch 2.8.0, 2.12.1, 2.13.0, 2.13.1,
2.14.0 and 3.0.0.
- The Windows wheel job only runs on `main` (`ci_mainline_only.yml`) and
installs torch 2.13 there, so it was validated by dispatching that
workflow against this branch.
---
.../utils/_build_optional_torch_c_dlpack.py | 22 ++++++++++++++--
tests/python/test_optional_torch_c_dlpack.py | 29 +++++++++++++++++++---
2 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/python/tvm_ffi/utils/_build_optional_torch_c_dlpack.py
b/python/tvm_ffi/utils/_build_optional_torch_c_dlpack.py
index 7277568d..9e4f9a16 100644
--- a/python/tvm_ffi/utils/_build_optional_torch_c_dlpack.py
+++ b/python/tvm_ffi/utils/_build_optional_torch_c_dlpack.py
@@ -589,6 +589,24 @@ def parse_env_flags(env_var_name: str) -> list[str]:
return []
+def get_cpp_standard() -> str:
+ """Get the C++ standard required to compile against the installed torch
headers.
+
+ Torch 2.13 started using C++20 constructs such as designated initializers
and
+ default member initializers for bit-fields in its public headers, and
builds its
+ own extensions with C++20 accordingly. Older versions build with C++17.
+
+ Returns
+ -------
+ standard : str
+ Either ``"c++17"`` or ``"c++20"``.
+
+ """
+ if torch.__version__ >= torch.torch_version.TorchVersion("2.13.0"):
+ return "c++20"
+ return "c++17"
+
+
def _run_build_on_linux_like(
build_dir: Path,
libname: str,
@@ -600,7 +618,7 @@ def _run_build_on_linux_like(
"""Build the module directly by invoking compiler commands (non-Windows
only)."""
from tvm_ffi.libinfo import find_dlpack_include_path # noqa: PLC0415
- default_cflags = ["-std=c++17", "-fPIC", "-O3", "-fvisibility=hidden"]
+ default_cflags = [f"-std={get_cpp_standard()}", "-fPIC", "-O3",
"-fvisibility=hidden"]
# Platform-specific linker flags
if IS_DARWIN:
# macOS uses @loader_path instead of $ORIGIN
@@ -650,7 +668,7 @@ def _generate_ninja_build_windows(
from tvm_ffi.libinfo import find_dlpack_include_path # noqa: PLC0415
default_cflags = [
- "/std:c++17",
+ f"/std:{get_cpp_standard()}",
"/MD",
"/wd4819",
"/wd4251",
diff --git a/tests/python/test_optional_torch_c_dlpack.py
b/tests/python/test_optional_torch_c_dlpack.py
index 2a27892d..398afdbb 100644
--- a/tests/python/test_optional_torch_c_dlpack.py
+++ b/tests/python/test_optional_torch_c_dlpack.py
@@ -110,6 +110,20 @@ def
test_existing_torch_dlpack_api_is_preferred_on_rocm(monkeypatch: pytest.Monk
assert _optional_torch_c_dlpack.load_torch_c_dlpack_extension() is None
+def _run_build(args: list[str]) -> None:
+ """Run the addon build script, surfacing its output when the build fails.
+
+ The build script reports compiler and linker errors through its own
stderr, so
+ capture it and attach it to the failure instead of only reporting the exit
code.
+ """
+ result = subprocess.run(args, capture_output=True, text=True, check=False)
+ if result.returncode != 0:
+ raise AssertionError(
+ f"Build failed with exit status {result.returncode}\n"
+ f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}"
+ )
+
+
@pytest.mark.skipif(torch is None, reason="torch is not installed")
def test_build_torch_c_dlpack_extension() -> None:
assert torch is not None
@@ -132,7 +146,7 @@ def test_build_torch_c_dlpack_extension() -> None:
args.append("--build-with-rocm")
else:
raise ValueError("Cannot determine whether to build with CUDA or
ROCm.")
- subprocess.run(args, check=True)
+ _run_build(args)
lib_path =
str(Path("./output-dir/libtorch_c_dlpack_addon_test.so").resolve())
assert Path(lib_path).exists()
@@ -153,13 +167,20 @@ def test_parallel_build() -> None:
processes = []
for i in range(num_processes):
p = subprocess.Popen(
- [sys.executable, str(build_script), "--output-dir", output_dir,
"--libname", libname]
+ [sys.executable, str(build_script), "--output-dir", output_dir,
"--libname", libname],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ text=True,
)
processes.append((p, output_dir))
for p, output_dir in processes:
- p.wait()
- assert p.returncode == 0
+ stdout, stderr = p.communicate()
+ if p.returncode != 0:
+ raise AssertionError(
+ f"Build failed with exit status {p.returncode}\n"
+ f"stdout:\n{stdout}\nstderr:\n{stderr}"
+ )
lib_path = str(Path(f"{output_dir}/{libname}").resolve())
assert Path(lib_path).exists()