`ITK_USE_64BITS_IDS` selects the width of `SizeValueType`,
`IdentifierType`, `In…dexValueType` and `OffsetValueType`. It is
inert on Linux and macOS, load-bearing on Windows and WebAssembly,
untested in one of its two states, and it caused a live wrapping
defect (#6772). This issue asks whether it should still exist.
**This is a deliberate parking issue.** No action is proposed for now —
the decision needs a proper ABI/downstream impact assessment that
nobody currently has time to do, and the deciding facts are not in the
repository. Everything needed to pick this up cold is recorded below,
so a future reader needs no other context.
Raised by @N-Dekker on #6772: *"Is it still useful nowadays? Why would
some users still want to switch off `ITK_USE_64BITS_IDS` on Windows,
nowadays?"*
---
## The one-paragraph version
`OFF` is not a Windows state — it is the **default everywhere except
64-bit Windows**. The option only changes any type where `long` and
`long long` differ in width, which is **Windows LLP64 and 32-bit
targets (today: WebAssembly)**; on LP64 Linux/macOS it is a no-op in
both positions. Forcing it `ON` globally would therefore change nothing
on Linux, macOS, or default Windows, and would land its entire cost on
WebAssembly — while still **not** producing a uniform type. Genuine
uniformity requires a wider change that carries a non-obvious ABI trap
on macOS (below).
<details>
<summary>Mechanics — exactly what the option does, with file:line</summary>
**Declaration** — `CMakeLists.txt:668-678`. Declared for all platforms;
only the default is platform-dependent:
```cmake
set(ITK_USE_64BITS_IDS_DEFAULT "OFF")
# Note WIN32 is true when targeting any windows system
if(CMAKE_SIZEOF_VOID_P EQUAL "8" AND WIN32)
set(ITK_USE_64BITS_IDS_DEFAULT "ON")
endif()
option(
ITK_USE_64BITS_IDS
"When ON, ITK will use 64-bit integer types instead of long types for sizes and indexes. This is needed for managing images larger than 4Gb in some platforms."
"${ITK_USE_64BITS_IDS_DEFAULT}"
)
mark_as_advanced(ITK_USE_64BITS_IDS)
```
**Propagation** — `Modules/Core/Common/src/itkConfigure.h.in:79`
(`#cmakedefine ITK_USE_64BITS_IDS`).
**Consumption** — `Modules/Core/Common/include/itkIntTypes.h:65`:
```cpp
#if defined(ITK_USE_64BITS_IDS) && ((ULLONG_MAX != ULONG_MAX) || (LLONG_MAX != LONG_MAX))
using SizeValueType = uint64_t; // IdentifierType = SizeValueType
using IndexValueType = int64_t;
using OffsetValueType = int64_t;
#else
using SizeValueType = unsigned long;
using IndexValueType = long;
using OffsetValueType = long;
#endif
```
The **second clause is the crux**: the 64-bit branch is taken only
where `long` and `long long` differ in width.
| Data model | Platforms | `long` | Option's effect |
|---|---|---|---|
| LP64 | Linux, macOS (64-bit) | 64 | **Inert** — both settings give 64-bit `SizeValueType` |
| LLP64 | Windows (64-bit) | 32 | **Live** — OFF = 32-bit ids, ON = 64-bit. Default ON |
| ILP32 | WebAssembly (`wasm32`, `wasm32-wasi`), 32-bit targets | 32 | **Live** — ON = 64-bit ids on a 32-bit address space. Default OFF |
That inertness on LP64 is why the #6772 mismatch survived from #3502
(July 2022) undetected: every platform where Python wrapping is
routinely built resolves `ITKM_IT` to `UL` either way.
WebAssembly is a live, actively-developed ITK target, not a
hypothetical — ITK's own `CMakeLists.txt:316` branches on
`if(WASI OR EMSCRIPTEN)`, and
[ITK-Wasm](https://github.com/InsightSoftwareConsortium/ITK-Wasm) is
current.
</details>
<details>
<summary>History — why the option exists</summary>
| Commit | Change |
|---|---|
| `feb637c0b09` | ENH: Restore ITK_USE_64BITS_IDS option |
| `fca71585536` | COMP: Enable the correct integer types for 64bits Ids |
| `07dfd5b9bd4` | COMP: Prevent ITK_USE_64BITS_IDS with Wrapping on Windows (ITK 4.4) |
| `8bb8c743267` | ENH: Use ITK_USE_64BITS_IDS for windows 64 by default (ITK 4.10) |
Release notes also record `COMP: Fix HDF5IO with ITK_USE_64BITS_IDS on
Windows` (4.3). The arc: the option existed; wrapping could not cope
with it on Windows so it was *prevented* there in 4.4; it was then made
the Windows default in 4.10 once >4 GB image support outweighed the
wrapping friction.
**Prior discussion:** Discourse
[#2053, *"Could ITK drop ITK_USE_64BITS_IDS and just do SizeValueType = std::size_t?"*](https://discourse.itk.org/t/could-itk-drop-itk-use-64bits-ids-and-just-do-sizevaluetype-std-size-t/2053)
(2019, 8 posts). Positions:
- **@N-Dekker** (opener) — proposed `SizeValueType = std::size_t` and
dropping the option, because the profusion of size/offset types makes
narrowing bugs hard to find. His PR #1087 fixed a batch; *"this is
just a tip of the iceberg."* Follow-up: `std::ptrdiff_t` for
`IndexValueType`/`OffsetValueType`.
- **@dzenanz** — *"This is very much overdue. I support this change."*
- **@blowekamp** — named the two use cases the option actually serves:
(1) **Win64 compatibility** with code historically assuming
`unsigned long`; (2) **32-bit architectures streaming large images.**
Noted the option's name is inaccurate and that he was unsure either
case was still relevant. Suggested adding `ITK_USE_SIZE_T_IDS` to
experiment rather than making a breaking change during 5.0
maintenance.
- **@matt.mccormick** — objected that `size_t` is 32-bit on 32-bit ARM
and ITK wanted to keep processing large images there via streaming.
- **@dzenanz** — the cited ARM porting guide was from 2011; 64-bit ARM
has 64-bit `size_t`. **@matt.mccormick** partly conceded: *"most of
the newer and high performance systems are 64-bit ARM … so size_t
could be fine in practice."*
Rough agreement in principle, then it stopped. `ITK_USE_SIZE_T_IDS` was
never added. Seven years on, the 32-bit-ARM premise that anchored the
main objection is weaker — but WebAssembly has since arrived as a new
ILP32 target, so the *category* of 32-bit concern is not empty.
</details>
<details>
<summary>What the option costs today</summary>
**1. A wrapping-visible type divergence.**
`Wrapping/WrapBasicTypes.cmake:222` is the sole place the mangled
identifier type is chosen:
```cmake
if(WIN32 AND ITK_USE_64BITS_IDS)
set(ITKM_IT ${ITKM_ULL})
else()
set(ITKM_IT ${ITKM_UL})
endif()
```
Note it keys on the CMake option and `WIN32`, **never on the actual C++
type**. #6772 exists solely because `itk/support/types.py` re-derived
this independently and disagreed on Windows with the option OFF.
**2. A filter silently missing from the Windows Python package.**
`Modules/Segmentation/Watersheds/wrapping/itkTobogganImageFilter.wrap`:
```cmake
# The itk::IdentifierType, which is unsigned long on Unix but can
# be unsigned long long on Windows when ITK_USE_64BITS_IDS is
# enabled, is not wrapped in the supporting classes on Windows.
if(NOT WIN32 OR NOT ITK_USE_64BITS_IDS)
itk_wrap_class("itk::TobogganImageFilter" POINTER)
```
Because the Windows default is ON, `itk.TobogganImageFilter` is
**absent from the standard Windows Python package** — a
platform-dependent API gap with no diagnostic.
**3. A configuration nothing tests.** No CI job builds Python wrapping
on Windows with `ITK_USE_64BITS_IDS=OFF`. #6772's defect was findable
only by reading CMake, never by running anything.
</details>
<details>
<summary>Options and trade-offs</summary>
**A. Remove the option; `IdentifierType` follows the platform.**
Removes the divergence, the Toboggan carve-out and the untested
configuration. But the Windows choice then has to be made *for* users:
hard-wiring 64-bit ids is an ABI break for anyone building OFF;
following `long` silently caps Windows at 32-bit ids — a functional
regression, and precisely what 4.10 fixed. Neither is free.
**B. Remove only the ability to set `OFF` on 64-bit Windows.**
Answers Niels' question directly and kills the divergence, the Toboggan
carve-out and the #6772 defect class in one step, while leaving the
option meaningful on ILP32/WASM. Narrowest blast radius. Cost: an ABI
break for any Windows consumer deliberately building OFF — existence
currently **unknown**. *Cheapest real improvement, contingent on the
open question below.*
**C. `SizeValueType = std::size_t`, `Index/Offset = std::ptrdiff_t`**
(the 2019 proposal). The only route to genuine uniformity and removes a
whole class of narrowing bugs. But see the ABI trap below — it is a
wider change than "same width, no problem" suggests, and would want
forest-build measurement of downstream cost first.
**D. Keep it, document it, test it.** Add a Windows CI job with
`ITK_USE_64BITS_IDS=OFF` + Python wrapping so the configuration stops
being unverified; un-`mark_as_advanced` it. Lowest risk; keeps all
three costs above.
**Explicitly considered and rejected: force `ON` for all platforms.**
It does not do what it appears to. Forcing the first clause of the
guard true leaves the second intact:
| Platform | Forced-`ON` result | Change vs today |
|---|---|---|
| Linux / macOS (LP64) | `unsigned long` (2nd clause false) | **none** |
| Windows 64-bit (LLP64) | `uint64_t` | none (already default) |
| WASM / ILP32 | `uint64_t` | **32-bit ids become 64-bit** |
So it is a no-op on the platforms where most ITK is built, a no-op on
the Windows default, and lands its entire cost on WebAssembly: 64-bit
arithmetic emulated in a 32-bit VM, `Index<3>`/`Offset<3>` doubling
12 → 24 bytes, larger binaries — where binary size is a first-order
concern. And it still would not deliver type uniformity, which is the
only thing that retires this bug class.
</details>
<details>
<summary>⚠ The ABI trap in any "just make it uint64_t" approach</summary>
Getting one type everywhere requires dropping the
`ULLONG_MAX != ULONG_MAX` clause too, making `SizeValueType = uint64_t`
unconditional. That is **not** width-neutral on LP64 — it is
type-identity-changing, and inconsistently between Linux and macOS:
```
$ c++ -std=c++17 probe.cxx && ./probe # macOS arm64, Apple clang
sizeof(long)=8 sizeof(long long)=8 sizeof(size_t)=8
ULLONG_MAX != ULONG_MAX -> 0
uint64_t is 'unsigned long' ? 0
uint64_t is 'unsigned long long' ? 1
```
On macOS `uint64_t` is `unsigned long long`; on Linux/glibc it is
`unsigned long`. Both 64 bits, but **distinct types** for overload
resolution, name mangling and template instantiation.
Concretely: `SizeValueType` on macOS would silently move from
`unsigned long` to `unsigned long long` while
`Wrapping/WrapBasicTypes.cmake:222` still computes `ITKM_IT` as `UL`,
because it keys on `WIN32 AND ITK_USE_64BITS_IDS` and not on the C++
type. That reproduces the exact #6772 defect class on macOS — where CI
would catch it — and on Linux, where it would not. **Any such change
must fix the wrapping mangling in the same commit.**
</details>
<details>
<summary>Reproduce / re-verify the claims above</summary>
```bash
# Option declaration and platform-dependent default
sed -n '668,678p' CMakeLists.txt
# The guard that makes it inert on LP64
sed -n '60,95p' Modules/Core/Common/include/itkIntTypes.h
# Every consumer in the tree (should be 5 files)
grep -rn "ITK_USE_64BITS_IDS" --exclude-dir=.git .
# The wrapping divergence
sed -n '215,230p' Wrapping/WrapBasicTypes.cmake
# The Toboggan carve-out
sed -n '1,12p' Modules/Segmentation/Watersheds/wrapping/itkTobogganImageFilter.wrap
# WASM is a supported target
grep -n -A3 "WASI OR EMSCRIPTEN" CMakeLists.txt
```
Type-identity probe used in the ABI-trap section:
```cpp
#include <cstdint>
#include <climits>
#include <typeinfo>
#include <cstdio>
int main(){
printf("sizeof(long)=%zu sizeof(long long)=%zu sizeof(size_t)=%zu\n",
sizeof(long),sizeof(long long),sizeof(size_t));
printf("ULLONG_MAX != ULONG_MAX -> %d\n", (ULLONG_MAX != ULONG_MAX));
printf("uint64_t is 'unsigned long' ? %d\n", (int)(typeid(uint64_t)==typeid(unsigned long)));
printf("uint64_t is 'unsigned long long' ? %d\n", (int)(typeid(uint64_t)==typeid(unsigned long long)));
}
```
</details>
---
## Open questions that must be answered before deciding
1. **Does anyone build `ITK_USE_64BITS_IDS=OFF` on 64-bit Windows, and
why?** @blowekamp's 2019 answer was Win64 compatibility with code
assuming `unsigned long`. If that constituency is empty, option B is
cheap and A becomes tractable. A Discourse post is the fastest way
to find out. **This is the single highest-value question here.**
2. **Does ITK-Wasm want 64-bit ids, and is the size/perf cost
acceptable on `wasm32`?** Determines whether the option must survive
for ILP32 at all.
3. **What is the downstream blast radius** of option C? Measurable with
the forest build across SimpleITK, Slicer, ANTs, BRAINSTools,
elastix.
## Suggested sequencing when this is picked up
1. Ask (1) and (2) on Discourse; wait for a real answer, not silence.
2. If (1) is empty → implement **B**, including deleting the Toboggan
carve-out, and add the Windows-wrapping CI job from **D**.
3. Only then evaluate **C** with forest-build evidence, and only with
`WrapBasicTypes.cmake` fixed in the same commit.
Related: #6772 (the wrapping mismatch that surfaced this), #6769,
#3502 (introduced `itk.IT`, July 2022, commit
`6a9aca9906b6660b1d04d7d6971453395c2dfc1d`), PR #1087, Discourse
[#2053](https://discourse.itk.org/t/could-itk-drop-itk-use-64bits-ids-and-just-do-sizevaluetype-std-size-t/2053).