I've come up against this a couple times, so thanks for addressing it!
Seems like 2a is the way to go. It's a relatively constrained issue, so it it seems like it should be something that can fixed with additions to the language with fix-it-able warnings for the places where it's ambiguous.
I would prefer some sort of symbol to indicate "treat this as a module name" over a # or @ option. As much as I'm hesitant to suggest something that looks like C++, ModuleName::ClassName seems like a good option.
It's foreign in Swift, but is a syntax that exists in other language and could be reasoned through if seen in the wild.
If nested modules are ever supported, the syntax would be self-explanatory while still concise: ParentModule::ParentModuleClass, ParentModule::ChildModule::ChildModuleClass
It would feel natural at both call site and as an import.
Hopefully there's a better sigil than :: that would probably be better. Unfortunately nothing better comes to mind immediately. I'm unsure how the C++ compatibility is coming, but I can see it potentially being used there (even if just internally).
I like this option well, and agree with the earlier reasoning about this type of disambiguation being rare. However, if we do want a sigil, I'd like to suggest @XCTest, as a callback to @import.
Would it be possible to have an non-prefixed Modules discriminator? Perhaps in the future it could expose API to see what modules are currently visible, but for now just be used to namespace the available modules? Modules.XCTest seems fine to me.
Perhaps using backticks to refer to a module explicitly would fit the language: `Module`.Something.
Swift already uses backticks to remove the ambiguity from using names that collide with keywords. So this could feel natural, as it would also remove ambiguity - just in the context of modules names.
Not sure if this could lead to any collisions with the already existing backtick functionality though.
If we want our design to be able to accommodate the possibility of submodules, a new scoping operator (either in name or in effect) is actually inevitable. It's just a question of how clunky it appears.
Consider that submodules may themselves need disambiguation; if we go with your idea, we have:
...and then .#. becomes a new scoping operator in all but name (particularly if we subscribe to @anandabits's point about option 2a versus 2b).
Likewise, if we go with something like Modules, then we have:
...and then .Modules. becomes a new scoping operator in all but name.
Compared to these options, the far superior choice in my view is ::; it's terse but not to the point of confusion, visually distinct as an actual operator that can be documented and taught, and precedented in other languages:
I think this deserve more thought. The reason you need two scoping operators is so you can avoid clashes with identifiers coming from two different uncoordinated sources. Wouldn't it make sense for submodules to be coordinated with the parent module so they don't cause clashes?
I think it should be possible to reduce the use of some form of scoping operator to only every need to occur once in such a chain.
In your example # is used as a kind of prefix. If we instead used it as a suffix, it would be clear that everything preceding the # are module names, and everything following it are member names.
E.g. ModuleName.SubModule.#.Member.Something would unambiguously state that ModuleName and SubModule are modules, and Member and Something are members.
In a way # would then act as a member-qualifer rather than a module-qualifier.
Is there a reason we would need a specific scoping operator, rather than just disambiguating by name, perhaps with a reserved top level indicator (Modules)? For import statements I would think that would work fine for both top level and theoretical submodules. e.g. import Modules.XCTest, import Modules.XCTest.Submodule. And if we disallow inline usage except in cases of required disambiguation, I don't think we need much in the way of special behavior.
I prefer $modules as the top level (or root) indicator. The rule will be: If there is an ambiguity, provide the full path: $modules.XCTest.... and I think it should be possible to provide fix-it for it.
I don't think we should let ad-hoc definition of submodules. This eliminates the possibility of module level declaration clashing with submodule name. They should be both under the control of the same module developer and the compiler should simply reject such name clashes.
Moreover, one day we may need some other top-level indicator. For example to support dynamically loaded or injected modules (ensuring they won't clash with statically present modules)
For the specific case of .swiftinterface files, where every name is fully qualified anyway, one option may be to change import to not add any of the imported module's members to the default namespace at all. (Although I guess this would only work if local definitions get fully qualified, too.)
For the general problem, my exotic syntax idea is to use regular parens and juxtaposition:
I find having the delimiters makes these easier to parse*, especially in Harlan's overloading example -- they are clearly little annotations on the names that follow them, and they are visually modest enough not to overwhelm the source text.
* That is, easy to parse for me as a human being, not necessarily the compiler.