From 5b6ab66e4325784d70ea70532c994faddcc0ea40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 16 Jan 2025 16:57:18 -0800 Subject: [PATCH] Sema: Ensure access-level on imports don't make non-visible decls visible Access-level on imports is designed to downgrade the visibility of imported decls. Add a check to apply this logic only to decls that were originally visible: public and same-package. rdar://143008763 --- lib/AST/Decl.cpp | 6 +- .../Sema/access-level-import-downgrades.swift | 156 ++++++++++++++++++ test/Sema/private-import-ambiguities.swift | 44 +++++ 3 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 test/Sema/access-level-import-downgrades.swift create mode 100644 test/Sema/private-import-ambiguities.swift diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 8585721e9665f..a741eb6353f5c 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4623,7 +4623,11 @@ getAccessScopeForFormalAccess(const ValueDecl *VD, if (localImportRestriction.has_value()) { AccessLevel importAccessLevel = localImportRestriction.value().accessLevel; - if (access > importAccessLevel) { + auto isVisible = access >= AccessLevel::Public || + (access == AccessLevel::Package && + useDC->getParentModule()->inSamePackage(resultDC->getParentModule())); + + if (access > importAccessLevel && isVisible) { access = std::min(access, importAccessLevel); resultDC = useDC->getParentSourceFile(); } diff --git a/test/Sema/access-level-import-downgrades.swift b/test/Sema/access-level-import-downgrades.swift new file mode 100644 index 0000000000000..78dedf45fb460 --- /dev/null +++ b/test/Sema/access-level-import-downgrades.swift @@ -0,0 +1,156 @@ +/// Ensure we only bump down the access-level of imported decls, not up. + +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +/// Build libraries +// RUN: %target-swift-frontend -emit-module %t/ImportedPrivate.swift \ +// RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -package-name pkg \ +// RUN: -emit-module-path %t/ImportedPrivate.swiftmodule +// RUN: %target-swift-frontend -emit-module %t/ImportedInternal.swift \ +// RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -package-name pkg \ +// RUN: -emit-module-path %t/ImportedInternal.swiftmodule -I %t +// RUN: %target-swift-frontend -emit-module %t/ImportedPackage.swift \ +// RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -package-name pkg \ +// RUN: -emit-module-path %t/ImportedPackage.swiftmodule -I %t +// RUN: %target-swift-frontend -emit-module %t/ImportedPublic.swift \ +// RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -package-name pkg \ +// RUN: -emit-module-path %t/ImportedPublic.swiftmodule -I %t + +/// Build clients +// RUN: %target-swift-frontend -typecheck %t/InPackageClient.swift -I %t \ +// RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -package-name pkg -verify +// RUN: %target-swift-frontend -typecheck %t/OutOfPackageClient.swift -I %t \ +// RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -verify + +//--- ImportedPrivate.swift + +private func privateFunc() {} +internal func internalFunc() {} +package func packageFunc() {} +public func publicFunc() {} + +//--- ImportedInternal.swift + +private func privateFunc() {} +internal func internalFunc() {} +package func packageFunc() {} +public func publicFunc() {} + +//--- ImportedPackage.swift + +private func privateFunc() {} +internal func internalFunc() {} +package func packageFunc() {} +public func publicFunc() {} + +//--- ImportedPublic.swift + +private func privateFunc() {} +internal func internalFunc() {} +package func packageFunc() {} +public func publicFunc() {} + +//--- InPackageClient.swift + +private import ImportedPrivate +internal import ImportedInternal +private import ImportedPackage +public import ImportedPublic // expected-warning{{public import of 'ImportedPublic' was not used in public declarations or inlinable code}} + +ImportedPrivate.privateFunc() // expected-error {{module 'ImportedPrivate' has no member named 'privateFunc'}} +ImportedPrivate.internalFunc() // expected-error {{module 'ImportedPrivate' has no member named 'internalFunc'}} +ImportedPrivate.packageFunc() +ImportedPrivate.publicFunc() + +ImportedInternal.privateFunc() // expected-error {{module 'ImportedInternal' has no member named 'privateFunc'}} +ImportedInternal.internalFunc() // expected-error {{module 'ImportedInternal' has no member named 'internalFunc'}} +ImportedInternal.packageFunc() +ImportedInternal.publicFunc() + +ImportedPackage.privateFunc() // expected-error {{module 'ImportedPackage' has no member named 'privateFunc'}} +ImportedPackage.internalFunc() // expected-error {{module 'ImportedPackage' has no member named 'internalFunc'}} +ImportedPackage.packageFunc() +ImportedPackage.publicFunc() + +ImportedPublic.privateFunc() // expected-error {{module 'ImportedPublic' has no member named 'privateFunc'}} +ImportedPublic.internalFunc() // expected-error {{module 'ImportedPublic' has no member named 'internalFunc'}} +ImportedPublic.packageFunc() +ImportedPublic.publicFunc() + +func funcContext() { + ImportedPrivate.privateFunc() // expected-error {{module 'ImportedPrivate' has no member named 'privateFunc'}} + ImportedPrivate.internalFunc() // expected-error {{module 'ImportedPrivate' has no member named 'internalFunc'}} + ImportedPrivate.packageFunc() + ImportedPrivate.publicFunc() + + ImportedInternal.privateFunc() // expected-error {{module 'ImportedInternal' has no member named 'privateFunc'}} + ImportedInternal.internalFunc() // expected-error {{module 'ImportedInternal' has no member named 'internalFunc'}} + ImportedInternal.packageFunc() + ImportedInternal.publicFunc() + + ImportedPackage.privateFunc() // expected-error {{module 'ImportedPackage' has no member named 'privateFunc'}} + ImportedPackage.internalFunc() // expected-error {{module 'ImportedPackage' has no member named 'internalFunc'}} + ImportedPackage.packageFunc() + ImportedPackage.publicFunc() + + ImportedPublic.privateFunc() // expected-error {{module 'ImportedPublic' has no member named 'privateFunc'}} + ImportedPublic.internalFunc() // expected-error {{module 'ImportedPublic' has no member named 'internalFunc'}} + ImportedPublic.packageFunc() + ImportedPublic.publicFunc() +} + +//--- OutOfPackageClient.swift + +private import ImportedPrivate +internal import ImportedInternal +private import ImportedPackage +public import ImportedPublic // expected-warning{{public import of 'ImportedPublic' was not used in public declarations or inlinable code}} + +ImportedPrivate.privateFunc() // expected-error {{module 'ImportedPrivate' has no member named 'privateFunc'}} +ImportedPrivate.internalFunc() // expected-error {{module 'ImportedPrivate' has no member named 'internalFunc'}} +ImportedPrivate.packageFunc() // expected-error {{module 'ImportedPrivate' has no member named 'packageFunc'}} +ImportedPrivate.publicFunc() + +ImportedInternal.privateFunc() // expected-error {{module 'ImportedInternal' has no member named 'privateFunc'}} +ImportedInternal.internalFunc() // expected-error {{module 'ImportedInternal' has no member named 'internalFunc'}} +ImportedInternal.packageFunc() // expected-error {{module 'ImportedInternal' has no member named 'packageFunc'}} +ImportedInternal.publicFunc() + +ImportedPackage.privateFunc() // expected-error {{module 'ImportedPackage' has no member named 'privateFunc'}} +ImportedPackage.internalFunc() // expected-error {{module 'ImportedPackage' has no member named 'internalFunc'}} +ImportedPackage.packageFunc() // expected-error {{module 'ImportedPackage' has no member named 'packageFunc'}} +ImportedPackage.publicFunc() + +ImportedPublic.privateFunc() // expected-error {{module 'ImportedPublic' has no member named 'privateFunc'}} +ImportedPublic.internalFunc() // expected-error {{module 'ImportedPublic' has no member named 'internalFunc'}} +ImportedPublic.packageFunc() // expected-error {{module 'ImportedPublic' has no member named 'packageFunc'}} +ImportedPublic.publicFunc() + +func funcContext() { + ImportedPrivate.privateFunc() // expected-error {{module 'ImportedPrivate' has no member named 'privateFunc'}} + ImportedPrivate.internalFunc() // expected-error {{module 'ImportedPrivate' has no member named 'internalFunc'}} + ImportedPrivate.packageFunc() // expected-error {{module 'ImportedPrivate' has no member named 'packageFunc'}} + ImportedPrivate.publicFunc() + + ImportedInternal.privateFunc() // expected-error {{module 'ImportedInternal' has no member named 'privateFunc'}} + ImportedInternal.internalFunc() // expected-error {{module 'ImportedInternal' has no member named 'internalFunc'}} + ImportedInternal.packageFunc() // expected-error {{module 'ImportedInternal' has no member named 'packageFunc'}} + ImportedInternal.publicFunc() + + ImportedPackage.privateFunc() // expected-error {{module 'ImportedPackage' has no member named 'privateFunc'}} + ImportedPackage.internalFunc() // expected-error {{module 'ImportedPackage' has no member named 'internalFunc'}} + ImportedPackage.packageFunc() // expected-error {{module 'ImportedPackage' has no member named 'packageFunc'}} + ImportedPackage.publicFunc() + + ImportedPublic.privateFunc() // expected-error {{module 'ImportedPublic' has no member named 'privateFunc'}} + ImportedPublic.internalFunc() // expected-error {{module 'ImportedPublic' has no member named 'internalFunc'}} + ImportedPublic.packageFunc() // expected-error {{module 'ImportedPublic' has no member named 'packageFunc'}} + ImportedPublic.publicFunc() +} diff --git a/test/Sema/private-import-ambiguities.swift b/test/Sema/private-import-ambiguities.swift new file mode 100644 index 0000000000000..0eade2b225142 --- /dev/null +++ b/test/Sema/private-import-ambiguities.swift @@ -0,0 +1,44 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +/// Build libraries +// RUN: %target-swift-frontend -emit-module %t/LibShared.swift \ +// RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -emit-module-path %t/LibShared.swiftmodule +// RUN: %target-swift-frontend -emit-module %t/LibWithPublicFoo.swift \ +// RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -emit-module-path %t/LibWithPublicFoo.swiftmodule -I %t +// RUN: %target-swift-frontend -emit-module %t/LibWithInternalFoo.swift \ +// RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -emit-module-path %t/LibWithInternalFoo.swiftmodule -I %t + +/// Build client +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -enable-library-evolution -swift-version 5 + +//--- LibShared.swift +public struct Struct { + public init() {} +} + +//--- LibWithPublicFoo.swift +import LibShared + +extension Struct { + public func foo() {} +} + +//--- LibWithInternalFoo.swift +import LibShared + +extension Struct { + internal func foo() {} +} + +//--- Client.swift +import LibShared +import LibWithPublicFoo +private import LibWithInternalFoo + +var s = Struct() +s.foo() // This is non-ambiguous