Skip to content

[6.1] Sema: Prioritize @_exported imports over local non-public imports #78101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2944,6 +2944,16 @@ SourceFile::getImportAccessLevel(const ModuleDecl *targetModule) const {
restrictiveImport = import;
}
}

// Reexports from the local module take precedence over non-public imports
// and lift all access-level restrictions. We still prioritize file local
// public imports as diagnostics will have an import to point to and
// they are recommended over indirect imports.
if ((!restrictiveImport.has_value() ||
restrictiveImport->accessLevel < AccessLevel::Public) &&
imports.isImportedBy(targetModule, getParentModule()))
return std::nullopt;

return restrictiveImport;
}

Expand Down
55 changes: 55 additions & 0 deletions test/Sema/access-level-import-exported.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/// Test that an @_exported import is preferred to local non-public imports.
// RUN: %empty-directory(%t)
// RUN: split-file --leading-lines %s %t

/// Build the libraries.
// RUN: %target-swift-frontend -emit-module %t/TargetLib.swift -o %t
// RUN: %target-swift-frontend -emit-module %t/IndirectLib.swift -I %t -o %t

/// Check acceptable client configurations to access TargetLib publicly.
// RUN: %target-swift-frontend -typecheck -verify -I %t \
// RUN: %t/Client_ImportDirect.swift
// RUN: %target-swift-frontend -typecheck -verify -I %t \
// RUN: %t/Client_ImportDirect.swift %t/Client_FileReexport.swift
// RUN: %target-swift-frontend -typecheck -verify -I %t \
// RUN: %t/Client_ImportDirectAsPrivate.swift %t/Client_FileReexport.swift
// RUN: %target-swift-frontend -typecheck -verify -I %t \
// RUN: %t/Client_ImportIndirectModule.swift
// RUN: %target-swift-frontend -typecheck -verify -I %t \
// RUN: %t/Client_ImportIndirectModule.swift %t/Client_FileReexport.swift
// RUN: %target-swift-frontend -typecheck -verify -I %t \
// RUN: %t/Client_ImportIndirectModuleAsPrivate.swift %t/Client_FileReexport.swift
// RUN: %target-swift-frontend -typecheck -verify -I %t \
// RUN: %t/Client_ImportIndirectLocal.swift %t/Client_FileReexport.swift

//--- TargetLib.swift
public struct TargetType {
public init() {}
}

//--- IndirectLib.swift
@_exported import TargetLib

//--- Client_FileReexport.swift
@_exported public import TargetLib

//--- Client_ImportDirect.swift
public import TargetLib
public func user(t: TargetType) {}

//--- Client_ImportDirectAsPrivate.swift
fileprivate import TargetLib
public func user(t: TargetType) {}
// Unrestricted as it's @_exported elsewhere in the module

//--- Client_ImportIndirectModule.swift
public import IndirectLib
public func user(t: TargetType) {}

//--- Client_ImportIndirectModuleAsPrivate.swift
fileprivate import IndirectLib
public func user(t: TargetType) {}
// Unrestricted as it's @_exported elsewhere in the module

//--- Client_ImportIndirectLocal.swift
public func user(t: TargetType) {}
21 changes: 18 additions & 3 deletions test/Sema/authoritative-import-priority.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,21 @@
/// Client testing order of preference for more levels of imports.
// RUN: %target-swift-frontend -typecheck -I %t \
// RUN: %t/ExportedClient_FileExported.swift %t/ExportedClient_FileA.swift \
// RUN: %t/ExportedClient_FileB.swift %t/ExportedClient_FileC.swift %t/ExportedClient_FileD.swift \
// RUN: %t/ExportedClient_FileB.swift %t/ExportedClient_FileC.swift \
// RUN: %t/ExportedClient_FileD_via_underlying.swift \
// RUN: -import-underlying-module -module-name ExportedClient \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -Rmodule-api-import -verify

// Same without the underlying clang module.
// RUN: %target-swift-frontend -typecheck -I %t \
// RUN: %t/ExportedClient_FileExported.swift %t/ExportedClient_FileA.swift \
// RUN: %t/ExportedClient_FileB.swift %t/ExportedClient_FileC.swift \
// RUN: %t/ExportedClient_FileD_via_exported.swift \
// RUN: -module-name ExportedClient \
// RUN: -enable-upcoming-feature InternalImportsByDefault \
// RUN: -Rmodule-api-import -verify

/// Client testing -public-module-name ordering.
// RUN: %target-swift-frontend -typecheck -I %t \
// RUN: %t/SwiftLibClient_FileA.swift %t/SwiftLibClient_FileB.swift \
Expand Down Expand Up @@ -156,11 +166,16 @@ public import NotLib
public func useTypesC(a: ExportedType) {}
// expected-remark @-1 {{struct 'ExportedType' is imported via 'NotLib', which reexports definition from 'LibCore'}}

//--- ExportedClient_FileD.swift
/// Last used the module-wide @_exported import.
//--- ExportedClient_FileD_via_underlying.swift
/// Then use the import of the underling clang module.
public func useTypesD(a: ExportedType) {}
// expected-remark @-1 {{struct 'ExportedType' is imported via 'ExportedClient', which reexports definition from 'LibCore'}}

//--- ExportedClient_FileD_via_exported.swift
/// Finally use the @_exported import from the local module.
public func useTypesD(a: ExportedType) {}
// It would be nice to have a remark even without an import to point to.


//--- SwiftLibClient_FileA.swift
/// Prefer the import matching public-module-name.
Expand Down