Skip to content

Gate the direct SwiftInfo pass-throughs on the layering features - #1879

Merged
adincebic merged 2 commits into
bazelbuild:mainfrom
mauriciogg:layering-passthrough-gates
Aug 4, 2026
Merged

Gate the direct SwiftInfo pass-throughs on the layering features#1879
adincebic merged 2 commits into
bazelbuild:mainfrom
mauriciogg:layering-passthrough-gates

Conversation

@mauriciogg

Copy link
Copy Markdown
Contributor

b054a97 (#1780) re-exports dependency SwiftInfos as direct in two places so the Swift layering check can see through targets that do not produce a module map:

  1. swift_clang_module_aspect's module-map-less path, as SwiftInfo(direct_swift_infos = direct_swift_infos + swift_infos)
  2. swift_library_group, as SwiftInfo(direct_swift_infos = get_providers(deps, SwiftInfo), ...)

This is causing OOM errors in Blaze in our repo ~350K targets since the list is materialized for every single target. This change gates the flattening of the swift_infos on the layering check feature (which we have disabled) so we can rebase our fork to the latest version.

b054a97 (bazelbuild#1780) re-exports dependency `SwiftInfo`s as *direct* in two
places so the Swift layering check can see through targets that do not
produce a module map:

1. `swift_clang_module_aspect`'s module-map-less path, as
   `SwiftInfo(direct_swift_infos = direct_swift_infos + swift_infos)`
2. `swift_library_group`, as
   `SwiftInfo(direct_swift_infos = get_providers(deps, SwiftInfo), ...)`

Both are semantically fine but quadratic at scale, because
`_swift_info_init` materializes each direct provider's `direct_modules`
into a fresh per-node list, and lists do not share structure the way
depsets do:

- Through chains of module-map-less aggregates (common with
  macro-generated wrapper targets), each level's `direct_modules` holds a
  fresh copy of everything below it.
- Through `swift_library_group`, every consumer of a group aggregating N
  modules loops over all N in
  `compilation_context_for_explicit_module_compilation` and merges N
  compilation contexts via `cc_common.merge_compilation_contexts`, per
  compile, with no memoization.

Measured on a large monorepo (~370k configured targets, explicit modules
enabled): full-app analysis went from 23 minutes followed by an OOM at a
64 GB heap inside `depset()` in `_swift_info_init` down to 54 seconds,
and a single leaf `swift_library` consumer went from an OOM at 30 GB to
~18 s / 325 MB, against ~2.4 GB retained on rules_swift 3.x. A Starlark
CPU profile of the ungated run attributed >60% of analysis CPU to
`compilation_context_for_explicit_module_compilation` and ~30% to the
aspect's `_handle_module`.

Only the layering checks consume the widened direct set, so gate both
pass-throughs on `swift.layering_check_swift` /
`swift.layering_check_for_c_deps` and restore the pre-bazelbuild#1780
direct/transitive split otherwise. Dependencies still reach consumers
through `transitive_modules`, so compilation is unaffected. The existing
layering-check tests enable those features and therefore still exercise
the pass-through path.

Note this gates the cost rather than removing it: a consumer that enables
a layering check on a large module graph still pays the quadratic
materialization. Making the layering check consume a depset instead of
materialized direct lists would be the complete fix.

@adincebic adincebic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

@adincebic
adincebic merged commit 784b625 into bazelbuild:main Aug 4, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants