Skip to content

fix(iOS): gate prebuilt headers on Swift C++-interop importability#57606

Open
chrfalch wants to merge 1 commit into
mainfrom
chrfalch/headers-verify-cxx-interop-gate
Open

fix(iOS): gate prebuilt headers on Swift C++-interop importability#57606
chrfalch wants to merge 1 commit into
mainfrom
chrfalch/headers-verify-cxx-interop-gate

Conversation

@chrfalch

Copy link
Copy Markdown
Collaborator

Summary

Adds a generator-time gate so a Swift-C++-interop-hostile C++ type can never silently ship in the prebuilt iOS core headers again.

The class this guards: the prebuilt core headers ship as real clang modules. A C++ value type reachable from a shipped module whose implicit copy constructor is declared but ill-formed on instantiation — e.g. an implicit-copy struct with a std::vector<move-only-T> member — compiles fine in C++ but hard-errors when a Swift target with -cxx-interoperability-mode=default imports the module and its generated bridging uses the type as a copyable value. This is invisible to C++ CI (plain C++ never instantiates the copy), so it only surfaced as a broken community-library nightly (react-native-unistyles, via TraceRecordingState / HostTracingProfile — fixed in #57605).

The gate: a fourth compile gate (stage 3d) in headers-verify.js's runCompileGates, compiling a Swift TU with -cxx-interoperability-mode=default that imports every module declared by the composed ReactNativeHeaders module map (parsed at runtime, not hardcoded), plus a probe that forces the ClangImporter copyability path for a small explicit list of value types (SWIFT_CXX_INTEROP_PROBE_TYPES). Because a bare import does not eagerly instantiate copy constructors on the current toolchain, the probe reproduces what a real interop consumer's generated code does: for each listed type it conditionally instantiates a copy, which fails exactly as the real consumer fails if the type regresses to an implicit copy. Adding future coverage is a one-line list edit.

One module (React_RCTAppDelegate) is excluded as non-importable under interop by design (ObjC bootstrap module with C++-only factory conformances); the inspector C++ graph it would reach is covered directly by the probe instead. The exclusion is documented in-source, and new exclusions are required to carry a justification.

Independent review confirmed the probe reproduces the real failure for the right reason (the dangerous shape reports is_copy_constructible_v == true, so the probe enters the branch and forces the ill-formed body instantiation — mirroring ClangImporter, not masking the bug), and that both offender types are covered.

⚠️ Land after #57605 — the gate runs against the composed artifacts, which are built from source; until the move-only type fixes are in, the gate correctly fails the prebuild on the unfixed types.

Changelog:

[INTERNAL] [ADDED] - Prebuild gate that fails composed iOS headers unusable from a Swift C++-interop consumer

Test Plan

Against a freshly composed Debug artifact (node scripts/ios-prebuild -c -f Debug):

  • Green: node scripts/ios-prebuild/headers-verify.js --flavor Debug passes the new gate with both probe types (TraceRecordingState, HostTracingProfile) fixed in the artifact.
  • Red, TraceRecordingState: restoring the unfixed TraceRecordingState.h into the composed artifact fails the gate with the exact __construct_at / implicit-copy diagnostic; restoring the fix → green.
  • Red, HostTracingProfile (proves coverage isn't accidental): same protocol with HostTracingProfile unfixed (TraceRecordingState left fixed) → gate fails naming HostTracingProfile and its probe specialization; restore → green.
  • node --check, prettier --check, flow focus-check clean. Gate is skipped by --skip-compile like the other compile gates.

🤖 Generated with Claude Code

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 20, 2026
@facebook-github-tools facebook-github-tools Bot added p: Expo Partner: Expo Partner Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. labels Jul 20, 2026
Add a compile gate to headers-verify.js that imports every composed
ReactNativeHeaders module under -cxx-interoperability-mode=default and
probes ClangImporter's copyability path for a list of value types, so a
C++ type with an ill-formed-on-instantiation implicit copy constructor
(invisible to C++ CI, breaks every Nitro-based Swift consumer) fails the
prebuild instead of a downstream community nightly. Covers the two known
offenders (TraceRecordingState, HostTracingProfile); future types are a
one-line list edit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chrfalch
chrfalch force-pushed the chrfalch/headers-verify-cxx-interop-gate branch from ce116a2 to 4265263 Compare July 21, 2026 11:11
@meta-codesync

meta-codesync Bot commented Jul 21, 2026

Copy link
Copy Markdown

@cipolleschi has imported this pull request. If you are a Meta employee, you can view this in D113011048.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. p: Expo Partner: Expo Partner Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants