What is a bystander module?

what is a “bystander module”? is it when another module adds a witness (table?) to another module, and that turns it into a bystander?

can a module have more than one bystander module? can there be more than one @ sign in the name of a symbolgraph file?

I might be missing the obvious here, but what's the context - I'm also not familiar with a bystander module, but I don't know where the term is coming from either.

it shows up in the DocC source code: swift-docc/Symbol.swift at 03b711a8ab90e8ca4945f7753d50960bfa48d316 · apple/swift-docc · GitHub

A code search reveals it is the same as bystander modules for cross import overlays. DocumentationNode.swift - apple/swift-docc - Sourcegraph

It looks like this should be part of the Lexicon.md file and it isn't but you can take a look at the RFC. NNNN-cross-import-overlays.md · GitHub

A cross-import overlay module wraps its declaring module , re-exporting the declaring module’s contents but also adding extra APIs to it. The compiler imports the cross-import overlay module in place of the declaring module only when an additional bystanding module is imported into the same file.

can a module have more than one bystander module?

I might be missing something, but I don't think so. If that were the case, these signatures would probably require passing a collection of bystander names instead of a single name. https://github.com/apple/swift/blob/4453571e461ba23679433024783ddbb3c3f6c164/lib/AST/Module.cpp#L1961-L1966

1 Like

why does swift-symbolgraph-extract emit so many files with @ signs if this is not a public feature yet?

I believe those happen not only for "bystander modules" (I have no experience with cross-import overlays) but also for extensions of symbols from other modules.

So if I had a module X with the following content:

extension String {
    func foo() { }
}

then swift-symbolgraph-extract would emit an X@Swift.symbols.json file with just that symbol in it. And that symbol wouldn't be in X.symbols.json. At least that's what I've observed with my own modules. The code for that seems to be here.

That's right. Symbol graph information for APIs in extensions to types that are declared outside the defining module is emitted in separate symbol graph files. The format is ModuleName@ExtendedModule.symbols.json.

2 Likes

is it guaranteed that every symbol in such a file will have a swiftExtension with an extendedModule that points to ExtendedModule?

Only if that file came from extensions, instead of a cross-import overlay. A cross-import overlay will have an @ in its filename, but it won't populate swiftExtension unless the symbol itself was written as an extension. In that case, the module information will have bystander-module information instead.

(Small detail: this can get complicated if that overlay itself defines extensions to modules outside either itself or the declaring module, since then you'll get a mouthful like _DeclaringModule_BystanderModule@ExtendedModule.symbols.json! Those will populate swiftExtension.)

1 Like

okay, so to clarify

symbols in _DeclaringModule_BystanderModule@ExtendedModule:

  • are all part of DeclaringModule
  • are all namespaced to ExtendedModule
  • all contain a swiftExtension field pointing to ExtendedModule
  • are only available when BystanderModule is available

symbols in _DeclaringModule_BystanderModule:

  • are all part of DeclaringModule
  • are all namespaced to BystanderModule
  • may contain a swiftExtension field pointing to BystanderModule
  • are only available when BystanderModule is available

symbols in DeclaringModule@ExtendedModule:

  • are all part of DeclaringModule
  • are all namespaced to ExtendedModule
  • all contain a swiftExtension field pointing to ExtendedModule

is this correct?

Because cross-import overlays are themselves a separate module, but treated as part of the declaring module, it's a bit more complicated than that. To fit in with existing tooling that looked for extension symbols alongside the symbols for the module being extended, you won't actually see _DeclaringModule_BystanderModule.symbols.json by itself.

So, to build up in reverse order:

Symbols in DeclaringModule@ExtendedModule.symbols.json:

  • Were all originally written in DeclaringModule
  • Are all namespaced to ExtendedModule (as they only affect symbols originally from ExtendedModule)
  • All contain a swiftExtension field pointing to ExtendedModule

Symbols in the overlay module _DeclaringModule_BystanderModule:

  • Will appear in the file _DeclaringModule_BystanderModule@DeclaringModule.symbols.json by defualt
  • Are namespaced to DeclaringModule by default
  • Only appear to a dependency when it imports both DeclaringModule and BystanderModule in its code

Symbols in the file _DeclaringModule_BystanderModule@DeclaringModule.symbols.json:

  • Were originally written in the overlay module _DeclaringModule_BystanderModule
  • Are all namespaced to DeclaringModule
  • May contain a swiftExtension field pointing to ExtendedModule
    • ...if the overlay applied an protocol to a type in ExtendedModule
    • Symbols with swiftExtension are symbols being added to a type in ExtendedModule by a protocol
  • Only appear to a dependency when it imports both DeclaringModule and BystanderModule in its code

Symbols in the file _DeclaringModule_BystanderModule@ExtendedModule.symbols.json:

  • Were originally written in the overlay module _DeclaringModule_BystanderModule
  • Are all namespaced to ExtendedModule
  • All contain a swiftExtension field pointing to ExtendedModule
  • Only appear to a dependency when it imports all of DeclaringModule, BystanderModule, and ExtendedModule (so it can use the symbol being extended)

Hopefully this makes sense.

i have to admit i’m even more confused than before.

to start off, what’s the difference between a bystander module and an extended module?

how can they be namespaced to DeclaringModule without an extension block if they were written in _DeclaringModule_BystanderModule?

if they are adding symbols to a type in ExtendedModule, wouldn’t they be namespaced to ExtendedModule, and not DeclaringModule?

This was originally answered in the RFC that @typesanitizer linked above:

A cross-import overlay module wraps its declaring module , re-exporting the declaring module’s contents but also adding extra APIs to it. The compiler imports the cross-import overlay module in place of the declaring module only when an additional bystanding module is imported into the same file.

The point of a cross-import overlay is to sneak some extra APIs into the declaring module when the bystander module is also imported, without requiring that the declaring module use the bystander as a dependency. That's the difference: The declaring module is what's being extended, and the bystander is along for the ride.

This is a special case for cross-import overlays, since they are considered to extend the declaring module.

My mistake; they are namespaced to ExtendedModule.

1 Like

why would they appear in _DeclaringModule_BystanderModule@DeclaringModule.symbols.json and not _DeclaringModule_BystanderModule@ExtendedModule.symbols.json?

Ah, now i see what you're pointing at. It looks like i mistyped the original post. It should read something like this (edits in bold):

There is no ExtendedModule in this scenario, since as you pointed out, otherwise it would be in _DeclaringModule_BystanderModule@ExtendedModule.symbols.json. The symbols are all namespaced to DeclaringModule, whether they're being added in the cross-import overlay or extending a symbol originally declared in DeclaringModule.

1 Like

if A, B, and C are ordinary modules, A != B, A != C, and _A_C is a module overlay, are the following statements true or false?


1.a. every symbol in A.symbols.json is namespaced to A.

1.b. every symbol in A.symbols.json that populates extendedModule populates it with A.


2.a. every symbol in A@B.symbols.json is namespaced to B.

2.b. every symbol in A@B.symbols.json populates extendedModule with B.


3.a. every symbol in _A_C@A.symbols.json is namespaced to A.

3.b. every symbol in _A_C@A.symbols.json that populates extendedModule populates it with A.

3.c. symbols in _A_C@A.symbols.json behave exactly like symbols in A.symbols.json, except they are only available in the vicinity of import C.


4.a. every symbol in _A_C@B.symbols.json is namespaced to B.

4.b. every symbol in _A_C@B.symbols.json populates extendedModule with B.

4.c. symbols in _A_C@B.symbols.json behave exactly like symbols in A@B.symbols.json, except they are only available in the vicinity of import C.

4.d. _A_C@B.symbols.json is a valid symbol graph even if B == C.