Introduce separately-imported stdlib overlays

The `_Concurrency` and `_StringProcessing` modules are implementation details of the standard library; to developers, their contents should behave as though they are declared directly within module `Swift`. This is the exact same behavior we expect of cross-import overlays, so treat these modules as though they are cross-import overlays with no bystanding module.

Because these modules don’t re-export the standard library, it’s also necessary to treat `Swift` as a separately imported overlay of itself; do so and make that actually work.
This commit is contained in:
Becca Royal-Gordon
2025-09-17 13:00:53 -07:00
parent 83629977da
commit 3abbfaa9cb
10 changed files with 93 additions and 27 deletions

View File

@@ -3,6 +3,19 @@
> [!NOTE]
> This is in reverse chronological order, so newer entries are added to the top.
## Swift (next)
* Concurrency-related APIs like `Task` and string-processing-related APIs like `Regex` can now be qualified by the name
`Swift`, just like other standard library APIs:
```swift
Swift.Task { ... }
func match(_ regex: Swift.Regex<(Substring)>) { ... }
```
The old `_Concurrency` and `_StringProcessing` names are still supported for backwards compatibility, and Embedded
Swift projects must still explicitly `import _Concurrency` to access concurrency APIs.
## Swift 6.2
* [SE-0472][]:

View File

@@ -1294,6 +1294,10 @@ public:
return const_cast<ASTContext *>(this)->getStdlibModule(false);
}
/// Names of underscored modules whose contents, if imported, should be
/// treated as separately-imported overlays of the standard library module.
ArrayRef<Identifier> StdlibOverlayNames;
/// Insert an externally-sourced module into the set of known loaded modules
/// in this context.
void addLoadedModule(ModuleDecl *M);

View File

@@ -840,6 +840,12 @@ ASTContext::ASTContext(
#define IDENTIFIER_WITH_NAME(Name, IdStr) Id_##Name = getIdentifier(IdStr);
#include "swift/AST/KnownIdentifiers.def"
Identifier stdlibOverlayNames[] = {
Id_Concurrency,
Id_StringProcessing
};
StdlibOverlayNames = AllocateCopy(stdlibOverlayNames);
// Record the initial set of search paths.
for (const auto &path : SearchPathOpts.getImportSearchPaths())
getImpl().SearchPathsSet[path.Path] |= SearchPathKind::Import;

View File

@@ -447,6 +447,16 @@ DeclContext *DeclContext::getModuleScopeContext() const {
void DeclContext::getSeparatelyImportedOverlays(
ModuleDecl *declaring, SmallVectorImpl<ModuleDecl *> &overlays) const {
if (declaring->isStdlibModule()) {
auto &ctx = getASTContext();
for (auto overlayName: ctx.StdlibOverlayNames) {
if (auto module = ctx.getLoadedModule(overlayName))
overlays.push_back(module);
}
overlays.push_back(declaring);
return;
}
if (auto SF = getOutermostParentSourceFile())
SF->getSeparatelyImportedOverlays(declaring, overlays);
}

View File

@@ -2374,28 +2374,41 @@ void ModuleDecl::findDeclaredCrossImportOverlaysTransitive(
if (auto *clangModule = getUnderlyingModuleIfOverlay())
worklist.push_back(clangModule);
auto addOverlay = [&](Identifier overlay) {
// We don't present non-underscored overlays as part of the underlying
// module, so ignore them.
if (!overlay.hasUnderscoredNaming())
return;
ModuleDecl *overlayMod =
getASTContext().getModuleByIdentifier(overlay);
if (!overlayMod && overlayMod != this)
return;
if (seen.insert(overlayMod).second) {
overlayModules.push_back(overlayMod);
worklist.push_back(overlayMod);
if (auto *clangModule = overlayMod->getUnderlyingModuleIfOverlay())
worklist.push_back(clangModule);
}
};
while (!worklist.empty()) {
ModuleDecl *current = worklist.back();
worklist.pop_back();
if (current->isStdlibModule()) {
for (auto overlay : getASTContext().StdlibOverlayNames) {
addOverlay(overlay);
}
}
for (auto &pair: current->declaredCrossImports) {
Identifier &bystander = std::get<0>(pair);
for (auto *file: std::get<1>(pair)) {
auto overlays = file->getOverlayModuleNames(current, unused, bystander);
for (Identifier overlay: overlays) {
// We don't present non-underscored overlays as part of the underlying
// module, so ignore them.
if (!overlay.hasUnderscoredNaming())
continue;
ModuleDecl *overlayMod =
getASTContext().getModuleByName(overlay.str());
if (!overlayMod)
continue;
if (seen.insert(overlayMod).second) {
overlayModules.push_back(overlayMod);
worklist.push_back(overlayMod);
if (auto *clangModule = overlayMod->getUnderlyingModuleIfOverlay())
worklist.push_back(clangModule);
}
addOverlay(overlay);
}
}
}
@@ -2432,6 +2445,12 @@ ModuleDecl::getDeclaringModuleAndBystander() {
if (!hasUnderscoredNaming())
return *(declaringModuleAndBystander = {nullptr, Identifier()});
// If this is one of the stdlib overlays, indicate as much.
auto &ctx = getASTContext();
if (llvm::is_contained(ctx.StdlibOverlayNames, getRealName()))
return *(declaringModuleAndBystander = { ctx.getStdlibModule(),
Identifier() });
// Search the transitive set of imported @_exported modules to see if any have
// this module as their overlay.
SmallPtrSet<ModuleDecl *, 16> seen;
@@ -2511,7 +2530,8 @@ bool ModuleDecl::getRequiredBystandersIfCrossImportOverlay(
auto *clangModule = declaring->getUnderlyingModuleIfOverlay();
auto current = std::make_pair(this, Identifier());
while ((current = current.first->getDeclaringModuleAndBystander()).first) {
bystanderNames.push_back(current.second);
if (!current.second.empty())
bystanderNames.push_back(current.second);
if (current.first == declaring || current.first == clangModule)
return true;
}

View File

@@ -163,11 +163,18 @@ void ModuleNameLookup<LookupStrategy>::lookupInModule(
moduleOrFile->getParentModule(), overlays);
if (!overlays.empty()) {
// If so, look in each of those overlays.
for (auto overlay : overlays)
lookupInModule(decls, overlay, accessPath, moduleScopeContext, options);
bool selfOverlay = false;
for (auto overlay : overlays) {
if (overlay == moduleOrFile->getParentModule())
selfOverlay = true;
else
lookupInModule(decls, overlay, accessPath, moduleScopeContext,
options);
}
// FIXME: This may not work gracefully if more than one of these lookups
// finds something.
return;
if (!selfOverlay)
return;
}
const size_t initialCount = decls.size();

View File

@@ -2182,11 +2182,9 @@ bool CompletionLookup::tryModuleCompletions(Type ExprType,
// If the module is shadowed by a separately imported overlay(s), look up
// the symbols from the overlay(s) instead.
SmallVector<ModuleDecl *, 1> ShadowingOrOriginal;
if (auto *SF = CurrDeclContext->getParentSourceFile()) {
SF->getSeparatelyImportedOverlays(M, ShadowingOrOriginal);
if (ShadowingOrOriginal.empty())
ShadowingOrOriginal.push_back(M);
}
CurrDeclContext->getSeparatelyImportedOverlays(M, ShadowingOrOriginal);
if (ShadowingOrOriginal.empty())
ShadowingOrOriginal.push_back(M);
for (ModuleDecl *M : ShadowingOrOriginal) {
RequestedResultsTy Request =
RequestedResultsTy::fromModule(M, Filter).needLeadingDot(needDot());

View File

@@ -17,3 +17,8 @@ func f(_ t : UnsafeCurrentTask) -> Bool {
@available(SwiftStdlib 5.1, *)
func g(_: _Concurrency.UnsafeCurrentTask) {}
// Should also be allowed since _Concurrency is a separately-imported overlay of
// the standard library.
@available(SwiftStdlib 5.1, *)
func h(_: Swift.UnsafeCurrentTask) {}

View File

@@ -408,7 +408,7 @@ struct GlobalType {}
@_Concurrency.MainActor
extension GlobalType {
@_Concurrency.MainActor static func ==(
@Swift.MainActor static func ==(
lhs: GlobalType,
rhs: GlobalType
) -> Bool { true }

View File

@@ -1,7 +1,10 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -Xfrontend -disable-access-control -o %t/Assert_Debug -Onone
// RUN: %target-build-swift %s -Xfrontend -disable-access-control -o %t/Assert_Release -O
// RUN: %target-build-swift %s -Xfrontend -disable-access-control -o %t/Assert_Unchecked -Ounchecked
//
// With -disable-access-control on, `_StringProcessing._internalInvariant` would shadow `Swift._internalInvariant`
// RUN: %target-build-swift %s -Xfrontend -disable-access-control -Xfrontend -disable-implicit-string-processing-module-import -o %t/Assert_Debug -Onone
// RUN: %target-build-swift %s -Xfrontend -disable-access-control -Xfrontend -disable-implicit-string-processing-module-import -o %t/Assert_Release -O
// RUN: %target-build-swift %s -Xfrontend -disable-access-control -Xfrontend -disable-implicit-string-processing-module-import -o %t/Assert_Unchecked -Ounchecked
//
// RUN: %target-codesign %t/Assert_Debug
// RUN: %target-codesign %t/Assert_Release
// RUN: %target-codesign %t/Assert_Unchecked