What ArrowMetal's differential matrix and its platform work found in other projects, and where each report stands. A row is added when a finding is confirmed by a test in this repository, and updated when the report is filed, answered or fixed. Nothing here is a complaint about another project: every entry is a divergence the matrix has to carry, and each has a test that fails on the day upstream fixes it, so the workaround can be removed.
Merged in Apache Arrow from this work: in C++, utf8_normalize composes for NFC and NFKC
(apache/arrow#51237, 26.0.0); in Go, the scalar aggregate
framework with the count and sum kernels (apache/arrow-go#1336),
the allocator alignment docs (#1302), the registry docs
(#1303), the NewDatumWithoutOwning contract
(#1320) and the ArraySpan.SetSlice null-count fix behind the
chunked Kleene results (#1306). Fixed by others from our reports: the boolean
fill-null offset (apache/arrow#51240, 26.0.0) and
winsorize on a sliced array (apache/arrow#51251).
Listed in Apache DataFusion's user guide: datafusion-arrowmetal on the
integrations list
(apache/datafusion#26004, merged 2026-10-09).
| Project | Finding | Our evidence | Report | Status |
|---|---|---|---|---|
| pyarrow 25.0.1 | pc.utf8_normalize decomposes whatever form says (its NFC output is NFD) |
test_pyarrow_utf8_normalize_ignores_its_form_option |
filed 2026-09-08: apache/arrow#51225; fixed by our apache/arrow#51237, merged 2026-09-10 | fixed in 26.0.0; the matrix moved to pyarrow 26.0.0 on 2026-10-15, its oracle for utf8_normalize is pc.utf8_normalize again and the pinning test is retired |
| pyarrow 25.0.1 | pc.pairwise_diff on a sliced array ignores ArrowArray.offset |
test_pyarrow_pairwise_diff_ignores_the_array_offset |
reported by others: apache/arrow#50524, fixed by #50858 | fixed in 26.0.0; the matrix moved to pyarrow 26.0.0 on 2026-10-15, hands the oracle the slice itself again and the pinning test is retired |
| pyarrow 25.0.1 | pc.fill_null_forward, pc.fill_null_backward, pc.replace_with_mask on a sliced boolean array ignore the offset |
test_pyarrow_boolean_fill_null_forward_ignores_the_array_offset |
filed 2026-09-08: apache/arrow#51223; fixed by a contributor's apache/arrow#51240, merged 2026-09-23 | fixed in 26.0.0; the matrix moved to pyarrow 26.0.0 on 2026-10-15, hands the oracle the slice itself again and the pinning test is retired |
| pyarrow 25.0.1 | pc.winsorize on a sliced array nulls the wrong rows and places values where the input has nulls |
test_pyarrow_winsorize_ignores_the_array_offset |
filed 2026-09-08: apache/arrow#51224; fixed by a contributor in apache/arrow#51251, merged 2026-09-28 | fixed in 26.0.0; the matrix moved to pyarrow 26.0.0 on 2026-10-15, hands the oracle the slice itself again and the pinning test is retired |
| pyarrow 25.0.1 | pc.binary_slice with its own default stop raises ArrowInvalid: Negative buffer resize |
test_pyarrow_binary_slice_overflows_on_its_own_default_stop |
filed 2026-09-08: apache/arrow#51222; the same overflow was fixed for utf8_slice_codeunits in #36575, the binary path was not; a contributor's fix apache/arrow#52451 opened 2026-10-08 |
filed, open; taken by a contributor 2026-09-16, fix pull request under review (changes requested 2026-10-13) |
| pyarrow 25.0.1 | pc.year_month_day and pc.iso_calendar crashed the process in the first matrix run |
the matrix never calls them; temporal_struct compares field by field (EVALUATION.md) |
withheld: not reproduced on 2026-09-07 under guard malloc | unexplained, observed once |
| pyarrow 25.0.1 | A filtered Parquet read drops NaN rows when the filter is true for NaN (!=, negated comparisons, is_nan when min == max, is_null(nan_is_null=True), isin([nan])) and the file has row-group statistics: min/max leave NaN out and the statistics guarantee assumes every value is inside them |
test_not_equal_keeps_a_nan_row_group_where_pyarrow_drops_it (python/tests/test_parquet_nested.py) |
filed 2026-09-24: apache/arrow#51491 | filed, open |
| Arrow (vendored date library) | DST rules stop being applied after the last tabulated transition (2037), so a summer instant in 2050 gets the winter offset, on a fat TZif file | finding 14 in EVALUATION.md, 144 matrix cases | tracked on apache/arrow#36110 (open since 2023), fat-file reproduction added 2026-09-08; #42157 covers slim files | open on #36110 |
| Apple Metal | the Feature Set Tables say the Apple9 family has "the full set of 64-bit atomic operations", but the shading-language headers admit device ulong only for atomic min and max: no 64-bit add, compare-exchange, load or store compile, whatever language version is asked for (measured on an M4 Max, macOS 26.6.2). Grouped sums and hash tables carry 32-bit workarounds because of it |
DECISIONS.md, DESIGN.md; the probe is attached to the report | filed 2026-09-20 through Feedback Assistant as FB24858110 (summary), with the compile probe and the three kernels that carry the workarounds | filed, open |
| Apple Metal | makeComputePipelineState fails sporadically on the "Apple Paravirtual device" GitHub-hosted runners expose |
retry in MetalContext.pipeline; two complete CI logs (runner image macos-15-arm64 20260829.0321.1): 110 pipeline-creation failures across 23 kernels in the swift test step of one run; in the other, 0 in swift test (31 of 34 tests skipped) and one in the bench step (gb_accumulate_priv); makeLibrary succeeding every time |
filed 2026-09-20 through Feedback Assistant as FB24858160 (summary), with both CI logs in full | filed, open |
| Apple Metal | a persistent GPU worker polling shared memory cannot be made to work: CPU/GPU coherence and the driver's submit-and-notify floor | RESIDENT.md, two measured probes | filed 2026-09-20 through Feedback Assistant as FB24858235 (summary), as a suggestion: a system-scope coherence primitive or a documented sub-20 µs submit path | filed, open |
| Apache Arrow JS | no C Data Interface export or import in the JavaScript library, so the Node binding builds the ArrowArray/ArrowSchema structs itself from a Vector's buffers; and Data.slice advances the values and offsets buffers but not the validity bitmap, leaving the row offset in Data.offset, which a C Data consumer must undo |
node/src/addon.cc (the rewind on import, the advance on export); TYPESCRIPT.md |
tracked on apache/arrow-js#88 (open since 2022); the addon's mapping and an offer to build import and export were posted 2026-09-08 and 2026-09-10 | open on #88, offer to build it posted |
| arrow R 25.0.0 | the C Data Interface entry points (Array$export_to_c, Array$import_from_c) exist but the only allocators for the structs they take (allocate_arrow_array(), allocate_arrow_schema()) are unexported and none of the four has an Rd topic (133 topics, none matching import/export), so a third-party package must use ::: or allocate the structs in its own C |
r/arrowmetal/src/shim.c allocates them itself; R.md |
tracked on apache/arrow#39793 (open since 2024); our apache/arrow#51236, opened 2026-09-08, exports and documents the allocators and the two methods | pull request reviewed 2026-09-16; the maintainer's choice of scope (export the allocators, or document the methods and point packages at nanoarrow) is open |
| arrow R 25.0.0 | Array$create() on an R double or integer vector borrows R's memory, whose data begins 48 bytes into a page-aligned allocation, so the values buffer sits at offset 48 mod 64: below the columnar spec's recommended 64-byte alignment and below the page alignment a GPU consumer needs, invisible to R users; measured 20/20 at 1M and 10M (am_buffer_alignment()) |
R.md; the import copy costs 1.39 ms (median) per 80 MB | filed 2026-09-08: apache/arrow#51227 | closed the same day by the R maintainer as expected behaviour (not planned) |
| Apache Arrow Go v18.7.0 | the compute package registers 85 scalar and vector functions and no aggregate at all (no sum, mean, min_max, count, no hash_*), and its package doc does not say so; every reduction and group-by oracle in the Go binding had to be a plain Go loop |
go/arrowmetal TestArrowGoHasNoAggregates; GO.md |
filed 2026-09-08: apache/arrow-go#1296; docs apache/arrow-go#1303 merged 2026-09-08; the scalar aggregate framework with count and sum, apache/arrow-go#1336, merged 2026-09-25; docs follow-up apache/arrow-go#1338 merged 2026-10-05 | filed, open; arrow-go main has its first two scalar aggregates since 2026-09-25 |
| Apache Arrow Go v18.7.0 | no allocator promises an alignment: NewGoAllocator buffers inherit Go's 8 KiB spans, so a buffer starts at offset 0 or 8192 within a 16 KiB page and page-aligned import is a coin flip whose odds depend on heap fragmentation (measured between 5/20 and 15/20 across runs); memory/mallocator (libc, NewMallocatorWithAlignment) does give a chosen alignment, which the report as first written missed, so the remaining ask is the documentation. Related and already closed: apache/arrow-go#70 (the C Data Interface storing unpinned Go pointers, which runtime.Pinner addresses) |
TestAllocatorAlignment; arrowmetal.PageAlignedAllocator is the binding's own answer to alignment |
filed 2026-09-08: apache/arrow-go#1297; the doc paragraph the maintainer asked for, apache/arrow-go#1302, merged 2026-09-08 | fixed upstream, issue closed |
| Apache Arrow Go v18.7.0 | compute.NewDatumWithoutOwning returns the same Datum type as an owning datum, so an over-release compiles and frees buffers under a live array with no error; its doc comment is truncated (datum.go:278, "convenience function.+-") |
reproduced while writing the Go timing harness; GO.md | filed 2026-09-08: apache/arrow-go#1298; our docs apache/arrow-go#1320 merged 2026-09-21 (commit 6f2e895), stating the contract for both call patterns | filed, open; the docs merged 2026-09-21; the type question stays on the issue, where a contributor proposed a non-owning datum whose Release() is a no-op |
| Apache Arrow Go v18.7.0 | and_kleene and or_kleene return false where the answer is null when ExecCtx.ChunkSize is below the input length (same code on main 281776d): ArraySpan.SetSlice carries a cached null count into the next slice, and the Kleene kernels write the slice's count into the shared span |
the reproducer attached to apache/arrow-go#1305 (fails on main at ChunkSize 1 and 2; passes with the one-line SetSlice fix, whole compute suite still green) |
filed 2026-09-08: apache/arrow-go#1305 with the reproducer; the one-line fix with a chunk-size test is our apache/arrow-go#1306, reviewed 2026-09-15 (the ASF header asked for was added the same day), approved and merged 2026-10-13 (commit 7090f9e); the issue was closed with it | fixed upstream on main by our #1306, not in a release yet; the test retires when the Go module moves to the release that carries it |
| Swift 6.3.3 | in a generic throws function compiled at -O in its own module, the closure passed to withUnsafeBytes uses the error register x21 as scratch around an Objective-C message send and exits by tail-calling memmove without restoring it, so a function with no reachable throw reports a phantom error and the program crashes retaining it. ArrowMetal's array initialiser hit it and carries a plain-loop workaround |
MetalArray.init(_:), FINDINGS.md; the two-module Metal-free reproducer posted on swiftlang/swift#90477 (-O fails, -Onone passes) |
reported by others: swiftlang/swift#90477, where the two-module Metal-free reproducer was added 2026-09-08; related #90874 | open upstream, reproducer posted |
Nothing found in pandas, numpy, Polars or DuckDB is their defect: the costs the matrix measures there are crossings and idioms, not bugs.
Kept so the same question is not asked twice.
| Project | What looked like a limitation | Why it is not one |
|---|---|---|
| arrow-rs 59.3 | max of a float column with NaN returns NaN while min returns the smallest non-NaN |
Its documented total order puts NaN above every value (apache/arrow-rs#101, closed 2022); pyarrow and ArrowMetal skip NaN instead. A cross-implementation divergence, pinned by rust/arrowmetal/tests/compute.rs and tabled in RUST.md |
| arrow-rs 59.3 | values buffers are page aligned only from about 64 KiB upward | That is the macOS allocator's mmap threshold, not arrow-rs; measured in rust/arrowmetal/tests/copy_rule.rs, which prints the table for the reader's machine |
| bit64 | as.vector() on an integer64 reinterprets the bit pattern as a double |
Documented in ?bit64::integer64 ("Limitations inherited from Base R"); as.numeric() is the route, named in R.md |
| arrow R 25.0.0 | an int64 scalar comes back as base integer when it fits 32 bits and bit64::integer64 otherwise |
Documented (arrow.int64_downcast); the package takes an integer64 = argument because of it |
| cargo 1.95 | a build script's rustc-link-arg does not reach dependents, so the safe crate needs its own three-line build.rs for the rpath |
By design (rust-lang/cargo#9554, closed after an FCP); the DEP_<links>_ mechanism is the sanctioned route and is what the crate uses |
| Project | Route | Public tracker |
|---|---|---|
| Apache Arrow (pyarrow, the vendored date library) | GitHub issues, and a pull request when the fix is ours | github.com/apache/arrow |
| Apache DataFusion | GitHub issues and pull requests; the user guide's integrations list is where community crates are recorded (apache/datafusion#26004, merged 2026-10-09) | github.com/apache/datafusion |
| Swift compiler | GitHub issues, pull requests welcome | github.com/swiftlang/swift |
| Apple Metal, the shading language, the driver | Feedback Assistant (private; the FB number is what goes in the Report column, and APPLE_REPORTS.md carries a readable summary of each report), the Apple Developer Forums for a public thread, a Developer Technical Support incident or a WWDC lab for a conversation | none: Metal is not open source and has no public issue tracker |
| Apple's open-source projects that touch this work (MLX, Swift packages) | GitHub, like any other project | github.com/ml-explore/mlx, github.com/apple |
- open: confirmed here, test in place, nothing filed.
- filed: the report link goes in the Report column with the date.
- fixed in X: upstream released the fix; the test that pins the workaround starts failing on that version and is retired with it, and the row stays as history.