Swift's name lookup is…messy. Very messy. This causes a number of problems, and today I'd like to talk about a potential solution for one of them.
Swift allows names to be shadowed by declarations in a nested scope. The idea is that, if you import WidgetKit to access its
Widget type, but you yourself declare a
Widget type, you can always access WidgetKit's version by writing
WidgetKit.Widget. But what if something shadows the name
WidgetKit? Then you can't fully qualify the names in
WidgetKit at all.
That might seem unlikely, but many frameworks don't use the
Kit suffix; in particular, it's quite common for a framework to be named after a type it provides, like the
XCTest class in the
XCTest framework. If you import a module like this, you have no way to fully qualify any of its names—if you say
XCTest.Something, it will look for
Something inside the
This problem is especially severe in swiftinterface files, where we would like to fully qualify all names; XCTest and a couple other modules are currently working around this with a special flag, but we'd like to handle this properly. However, we don't want to give name lookup different semantics in swiftinterface files, and this problem does come up in developer-written code sometimes too, so some kind of language change is needed to address this problem.
Although the problem is clear, the solution is not, so I'd like to open discussion on that.
I can think of three ways to deal with this:
Change the semantics of all name lookups in some way, for instance by looking up module names before imported module contents.
Add a syntax which unambiguously indicates that the first identifier in a dotted name is a module name. For instance, using a straw syntax,
XCTest.Somethingmight refer to
@qualified XCTest.Somethingwould refer to
XCTestmodule. This approach has two variants:
a. Continue to prefer the current undifferentiated names wherever possible, but provide this new syntax as an escape hatch to be used when necessary.
b. Deprecate the use of undifferentiated module names and encourage developers to use the new syntax for all fully-qualified names. We might even remove support for undifferentiated module names in a future source-breaking version of Swift.
I don't think we can seriously consider #1 for two reasons: It would be badly source-breaking and the changed semantics would probably trade precision for convenience. (For instance, you might always need to say
XCTest.XCTest to address the
Both variants of #2 seem workable, but they would have different syntactic trade-offs—if we chose 2b, we would want a short, unobtrusive syntax since it would be used frequently, whereas for 2a, we would want something longer and more self-identifying since it would be used only when necessary. 2a changes the feel of the language less, but 2b simplifies the language. I think I prefer 2a, but I could probably be persuaded otherwise.
The other open question is, what should the "this is a module name" syntax look like? I'm prototyping this with the syntax
@qualified XCTest.Something because the compiler already understands type attributes, but there are a lot of other directions we could go:
Different spellings of a type attribute
Type attribute containing the module, like
Magic root identifier, like
Similarly, some sort of parameterized thing, like
Sigil indicating the following is a module name, like
Different symbol to look up a name inside a module vs. a type/instance, like
Straight single-quotes mean a module name, like
'XCTest'.Something; this would obviously preclude use for character literals
I'm open to other suggestions too, so bikeshed away.