SE-0491: Module selectors for name disambiguation

Hello Swift community,

The review of SE-0491: Module selectors for name disambiguation begins now and runs through September 30, 2025.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to the review manager via the forum messaging feature. When contacting the review manager directly, please keep the proposal link at the top of the message.

Trying it out

Toolchains including the implementation of this feature are available for macOS, Linux, and Windows.

In addition to the toolchain, reviewers will also need to use the -enable-experimental-feature ModuleSelector flag to try out the feature.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  • What is your evaluation of the proposal?
  • Is the problem being addressed significant enough to warrant a change to Swift?
  • Does this proposal fit well with the feel and direction of Swift?
  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available here.

Thank you,

Freddy Kellison-Linn
Review Manager

34 Likes

A strong +1 from me.

However, I feel that :: smells strange in Swift and its postfix appearance looks a bit akward. :slight_smile:

_ = RocketEngine::Fuel()    // Picks up the `Fuel` in `RocketEngine`, bypassing
                            // any other `Fuel`s that might be in scope

Would this feel more natural, prefixing a$ to the module name?

_ = $RocketEngine.Fuel()    // Picks up the `Fuel` in `RocketEngine`, bypassing
                            // any other `Fuel`s that might be in scope

1 Like

The problem is that $ prefixes are already valid on some declarations—for example, projected values of property wrappers—so those declarations could end up shadowing module names.

8 Likes

I feel that :: smells strange in Swift

Being hard to mistake operators for each other is a good thing.

As someone who used C++ before, I think :: is a good choice.

11 Likes

I don’t think :: is a bad choice, although not my preferred.


I wonder what’s the meaning of the comment in this rejected alternative:

Mission.#module(NASA).Booster.Exhaust    // Much wordier, not implementable as a macro

Are any of the other syntaxes “implementable as a macro”? Or is there an assumption that since it starts with # it should be implemented as a macro? Many of the # things in Swift are not macros.

3 Likes

+1 :100:

I think :: is a good unambiguous choice. :grin: Years ago, running into the problem, I tried using :: which obviously didn’t work.

Also, this now works?

typealias Complex = Complex::Complex<Double>`

5 Likes

Does :: work just as well as #module(…) if we decide to extend this as an infix syntax to disambiguate between method overloads that come from different modules?

+1

My only nit-picky concern is the precedence rules when :: is used on a member type. This is also shortly discussed in the Alternatives Considered section, though in a slightly different form.

Specifically, in one of the examples ‘Spacecraft.IonThruster::Engine’, is parsed as ‘Spacecraft.(IonThruster::Engine)‘, though it seems more natural when reading the code, to parse it as ‘(Spacecraft.IonThruster)::Engine‘, as that is how operators are generally applied in Swift. The ‘Spacecraft.(IonThruster::Engine)‘ order has some merit, in that :: can be considered a higher order operator working on modules, and thus should reside higher than ‘.’ and regular swift operators.
Still, to eliminate a possible point of confusion when reading (and writing) the code. I propose requiring parenthesis when using :: on members, i.e. the above example should then be written ‘Spacecraft.(IonThruster::Engine)’ to be valid.

6 Likes

The point is that #module(…) looks like a macro, but it’s not actually possible to implement a macro that does what it does, nor does it grammatically function like a freestanding macro (which builtins like #keyPath(…) and #isolation do).

Also, we want to use this feature in freestanding macro syntax; how would you do that? ##module(SomeMod).someMacro? #module(SomeMod) #someMacro? I can’t come up with anything that isn’t bizarre.

Keep in mind that Swift does not currently allow you to say, for instance, Spacecraft.(Engine). Allowing parentheses around member names is an extension of the existing syntax, and tbh kind of a weird one since it looks a lot like call parentheses.

8 Likes

A very good point I failed to notice. #SomeMod::someMacro is indeed clearly superior for invoking a macro in module SomeMod. The same could also be said of @SomeMod::SomeMacroOrPropertyWrapperOrResultBuilder.

3 Likes

This looks great! I didn’t see any explicit examples of qualified member references in expression context, like foo.Bar::baz(), but I’m assuming this is supported based on the grammar segment?

Also, I think we should call out and ban module qualification on dependent member types, so T.Foo::Bar inside a func f<T>() should not be allowed.

1 Like

What is your evaluation of the proposal?

+1

Is the problem being addressed significant enough to warrant a change to Swift?

Yes. In my own experience, I’ve found that malformed swiftinterfaces due to a type name shadowing a module name (and the similar issues described in the proposal) occur frequently enough in practice, and are difficult enough for users to work around in practice, to justify a change to the language. Independently, being able to solve this problem in user-authored code closes a number of small expressivity gaps in the language.

Does this proposal fit well with the feel and direction of Swift?

Yes, I think the proposal does a great job of enumerating a number of alternatives and justifying why the currently proposed syntax is best. I agree with the decision to focus the proposal strictly on module disambiguation and treating conformance/etc. disambiguation as future work.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Syntactically this proposal is very similar to C++, but the semantics are very different. Given that this syntax is unlikely to be pervasive in user-authored code, I think this is fairly unlikely to cause confusion, and where it does appear I think the meaning will be intuitively clear.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A relatively quick reading after following some of the earlier discussions.


I have one question which Is very much an edge case, but the proposal says:

When a reference to a declaration is prefixed by a module selector, only declarations declared in, or re-exported by, the indicated module will be considered as candidates.

Let’s say I have modules A and B. A re-exports B, and both A and B declare a ‘Thing’. Is A::Thing always guaranteed to refer to the ‘Thing’ in A?

2 Likes

That’s not something I considered. What’s your motivation? My first instinct is that an associated type could end up being shadowed by an extension in another module, so it might be useful to qualify them.

Swift’s usual rule is that a declaration directly in a given module will shadow a declaration it re-exports, and that will still apply here, so yes, A::Thing should refer to the Thing in A.

5 Likes

I am somewhat worried about :. Would this be encoded into the name itself? This is a problem because DocC encodes that into filenames and this is not a valid character on all file systems which prevents Windows support. Perhaps something else can be used?

2 Likes

Why? T might conform to a protocol with extensions in modules Foo and Foo2 that both add a Bar member.

1 Like

The :: affects lookup, it is not part of the name of the value.

2 Likes

Right, but I wanted to ensure that this is never something that would be serialised out in any form, including documentation (e.g. protocol conformances).

I look at this the other way around.

The symbol “#” was chosen for macros in order to make them look like compiler magic.

Swift has always used “#” for specifying things at compile time, such as “#if”, “#file”, and “#available”. When macros were introduced, they were given the same symbol because they also do things at compile time. They fit the existing pattern, so they are spelled in a similar manner.

I don’t know if “#module(...)” is the right spelling for this proposal, but I see the precedent set by macros as evidence in favor of doing so.

5 Likes

+1 from me as well. This addresses one of the longest standing problems in the ecosystem. It will make our builds and packages more robust by eliminating a kind of source breaks that sometimes isn’t workaroundable by developers. I am als interested if this has impact on documentation tooling similar to what @compnerd alluded to w.r.t. file naming and URLs.

It’s precisely because there is no shadowing in the semantic model here. Suppose you have this:

/// module X
protocol P { associatedtype A: Hashable }

/// module Y
protocol Q { associatedtype A: Comparable }

/// module Z
func f<T: P & Q>(_: T)

There is only one T.A in f(), and it’s both Hashable and Comparable. So it would not make sense to distinguish between T.X::A and T.Y::A.

3 Likes