unfortunately, serving documentation for synthetics is an inherently dynamic problem, and a static solution like emitting synthesized symbols into a symbol graph does not actually solve it.
i spent a lot of time trying to wrap my head around this concept, and i think i’ve finally zeroed in on the error in thinking that was causing me so many headaches with inherited members, which i’ll summarize below:
-
false statement: synthetic symbols are part of a module’s symbolgraph, and every module comes with a set of natural symbols and synthetic symbols. the API of a collection of modules (e.g., a package) is the union of the symbolgraphs of its constituent modules.
-
true statement: synthetic symbols are not part of any symbolgraph; rather, they arise through interactions between arbitrary subsets of modules. the API of a collection of modules (e.g., a package) cannot be computed in advance without considering every possible subset of modules in that collection.
to walk through a concrete example, suppose we have four single-module packages with a “Z”-shaped dependency graph:
legend: swift-x <- swift-y ::= “swift-y depends on swift-x”
swift-foo <- swift-baz
↙
swift-bar <- swift-qux
-
swift-foo
declaresenum FooType
-
swift-bar
declaresprotocol Barable
-
swift-baz
conformsFooType
toBarable
-
swift-qux
extendsBarable
withBarable.qux(_:)
the natural symbolgraph for these four packages might look like
swift-foo swift-bar swift-qux
FooType -- conforms to -> Barable -- has member -> Barable.qux(_:)
(perpetrator: swift-baz) (perpetrator: swift-qux)
now, you would expect that if you import
both BazModule
and QuxModule
, then FooType
should have a synthetic member FooType.qux(_:)
.
swift-foo swift-???
FooType -- has synthetic member -> FooType.qux(_:)
(perpetrator: swift-???)
but this symbol doesn’t actually belong to either of swift-{foo, bar, baz, qux}
, nor can we really “blame” its existence on any single module. it actually belongs to an imaginary client package that imports both BazModule
and QuxModule
.
swift-foo <- swift-baz <- (swift-baz × swift-qux)
↙ ↙
swift-bar <- swift-qux
so, at a minimum, the number of possible imaginary client modules grows with O(n2) of the number of modules involved.