From 3a7cf4747c4c64d3c5d6b4f4d8f8c2452c25fb38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 18 Sep 2025 09:21:14 -0700 Subject: [PATCH 1/2] Sema: Reorder the cases of LibraryLevel from less to more public --- include/swift/Basic/LangOptions.h | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 2e8e49ad79113..ae0996cd8794e 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -68,22 +68,24 @@ namespace swift { Complete, }; - /// Access or distribution level of a library. + /// Intended distribution level of a module. + /// + /// Ordered from more private to more public. enum class LibraryLevel : uint8_t { - /// Application Programming Interface that is publicly distributed so - /// public decls are really public and only @_spi decls are SPI. - API, - - /// System Programming Interface that has restricted distribution - /// all decls in the module are considered to be SPI including public ones. - SPI, + /// This isn't a library or the library distribution intent is unknown. + Other, - /// Internal Programming Interface that is not distributed and only usable - /// from within a project. + /// Internal Programming Interface: the module is not distributed and + /// only usable from within its project. IPI, - /// The library has some other undefined distribution. - Other + /// System Programming Interface: the module has restricted distribution, + /// all public decls in the module are considered to be SPI. + SPI, + + /// Application Programming Interface: the module is distributed publicly, + /// public decls are really public and only @_spi decls are SPI. + API, }; enum class AccessNoteDiagnosticBehavior : uint8_t { From 8b38d29333ad9eee55bf4868144239a9ae086d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Tue, 16 Sep 2025 09:53:55 -0700 Subject: [PATCH 2/2] Sema: Limit error on LE importing non-LE modules to SDK modules Limit reporting as an error imports of a non-library-evolution module from a library-evolution enabled module to sources that are part of the SDK. The error also requires having enabled `InternalImportsByDefault`. This should help prevent SDK breaking regressions while loosening the restriction elsewhere. Framework owners can enable `-library-level api` or `spi` to get this check as an error, along with more sanity checks. Other cases remain as a warning. We should look to silence it in some cases or offer a flag to do so. rdar://160414667 --- lib/Sema/ImportResolution.cpp | 17 ++++++---- test/ModuleInterface/imports-swift7.swift | 32 +++++++++++++++++-- ...ccess-level-and-non-resilient-import.swift | 19 +++++------ 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/lib/Sema/ImportResolution.cpp b/lib/Sema/ImportResolution.cpp index e14ef37dfb046..1c920b3966602 100644 --- a/lib/Sema/ImportResolution.cpp +++ b/lib/Sema/ImportResolution.cpp @@ -908,8 +908,6 @@ void UnboundImport::validateResilience(NullablePtr topLevelModule, targetName, importerName); if (ctx.LangOpts.hasFeature(Feature::InternalImportsByDefault)) { - // This will catch Swift 6 language mode as well where - // it will be reported as an error. inFlight.fixItRemove(import.accessLevelRange); } else { SourceRange attrRange = import.accessLevelRange; @@ -917,12 +915,17 @@ void UnboundImport::validateResilience(NullablePtr topLevelModule, inFlight.fixItReplace(attrRange, "internal"); else inFlight.fixItInsert(import.importLoc, "internal "); - - // Downgrade to warning only in pre-Swift 6 mode and - // when not using the experimental flag. - if (!ctx.LangOpts.hasFeature(Feature::AccessLevelOnImport)) - inFlight.limitBehavior(DiagnosticBehavior::Warning); } + + // Report as an error when InternalImportsByDefault is enabled or + // the experimental AccessLevelOnImport (but not Swift 6), only in libraries + // that are meant to be distributed. + auto featureEnabled = + ctx.LangOpts.hasFeature(Feature::AccessLevelOnImport) || + ctx.LangOpts.hasFeature(Feature::InternalImportsByDefault); + if (!featureEnabled || + SF.getParentModule()->getLibraryLevel() < LibraryLevel::SPI) + inFlight.limitBehavior(DiagnosticBehavior::Warning); } void UnboundImport::diagnoseInvalidAttr(DeclAttrKind attrKind, diff --git a/test/ModuleInterface/imports-swift7.swift b/test/ModuleInterface/imports-swift7.swift index 3b10ede862313..ddb6c4c53bbdf 100644 --- a/test/ModuleInterface/imports-swift7.swift +++ b/test/ModuleInterface/imports-swift7.swift @@ -5,8 +5,31 @@ // RUN: %target-swift-frontend -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -emit-module -o %t/nonResilient.swiftmodule %t/empty.swift // RUN: %target-swift-frontend -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -emit-module -o %t/resilient.swiftmodule %t/empty.swift -enable-library-evolution -/// Check errors. -// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %t/clientWithError.swift -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -I %t -verify -enable-upcoming-feature InternalImportsByDefault +/// Warning vs error on public import of a non-LE module from LE. +// RUN: %target-swift-emit-module-interface(%t.swiftinterface) \ +// RUN: %t/clientWithError.swift -I %t -verify \ +// RUN: -disable-implicit-concurrency-module-import \ +// RUN: -disable-implicit-string-processing-module-import \ +// RUN: -enable-upcoming-feature InternalImportsByDefault \ +// RUN: -verify-additional-prefix library-level-private- +// RUN: %target-swift-emit-module-interface(%t.swiftinterface) \ +// RUN: %t/clientWithError.swift -I %t -verify \ +// RUN: -disable-implicit-concurrency-module-import \ +// RUN: -disable-implicit-string-processing-module-import \ +// RUN: -enable-upcoming-feature InternalImportsByDefault \ +// RUN: -verify-additional-prefix library-level-private- -library-level ipi +// RUN: %target-swift-emit-module-interface(%t.swiftinterface) \ +// RUN: %t/clientWithError.swift -I %t -verify \ +// RUN: -disable-implicit-concurrency-module-import \ +// RUN: -disable-implicit-string-processing-module-import \ +// RUN: -enable-upcoming-feature InternalImportsByDefault \ +// RUN: -verify-additional-prefix library-level-public- -library-level spi +// RUN: %target-swift-emit-module-interface(%t.swiftinterface) \ +// RUN: %t/clientWithError.swift -I %t -verify \ +// RUN: -disable-implicit-concurrency-module-import \ +// RUN: -disable-implicit-string-processing-module-import \ +// RUN: -enable-upcoming-feature InternalImportsByDefault \ +// RUN: -verify-additional-prefix library-level-public- -library-level api /// Check Swift 7 imports printed in swiftinterface from 2 source files. // RUN: %target-swift-emit-module-interface(%t.swiftinterface) %t/main.swift %t/main-other.swift -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -I %S/Inputs/imports-clang-modules/ -I %t -verify -enable-upcoming-feature InternalImportsByDefault @@ -38,8 +61,11 @@ public import D // expected-warning {{public import of 'D' was not used in publi public import NotSoSecret // expected-warning {{'NotSoSecret' inconsistently imported as implementation-only}} // expected-warning @-1 {{public import of 'NotSoSecret' was not used in public declarations or inlinable code}} @_implementationOnly import NotSoSecret2 // expected-note {{imported as implementation-only here}} + //--- clientWithError.swift -@_exported public import nonResilient // expected-error {{module 'nonResilient' was not compiled with library evolution support; using it means binary compatibility for 'clientWithError' can't be guaranteed}} +@_exported public import nonResilient +// expected-library-level-public-error @-1 {{module 'nonResilient' was not compiled with library evolution support; using it means binary compatibility for 'clientWithError' can't be guaranteed}} +// expected-library-level-private-warning @-2 {{module 'nonResilient' was not compiled with library evolution support; using it means binary compatibility for 'clientWithError' can't be guaranteed}} // CHECK-7-NOT: import // CHECK-7: {{^}}public import A{{$}} diff --git a/test/Sema/access-level-and-non-resilient-import.swift b/test/Sema/access-level-and-non-resilient-import.swift index 8036c12957b99..b0d5d13599605 100644 --- a/test/Sema/access-level-and-non-resilient-import.swift +++ b/test/Sema/access-level-and-non-resilient-import.swift @@ -13,24 +13,25 @@ // RUN: %target-swift-frontend -emit-module %t/PrivateLib.swift -o %t /// A resilient client will error on public imports. -/// Keep AccessLevelOnImport to get the error from Swift 6 in Swift 5. +/// Use AccessLevelOnImport or InternalImportsByDefault to get the error +/// from Swift 7 in Swift 5. // RUN: %target-swift-frontend -typecheck %t/Client_Swift5.swift -I %t \ // RUN: -enable-library-evolution -swift-version 5 \ // RUN: -enable-experimental-feature AccessLevelOnImport -verify \ -// RUN: -package-name pkg -// RUN: %target-swift-frontend -typecheck %t/Client_Swift6.swift -I %t \ +// RUN: -package-name pkg -library-level api +// RUN: %target-swift-frontend -typecheck %t/Client_Swift7.swift -I %t \ // RUN: -enable-library-evolution \ // RUN: -enable-upcoming-feature InternalImportsByDefault \ -// RUN: -verify -package-name pkg +// RUN: -verify -package-name pkg -library-level api /// A non-resilient client doesn't complain. // RUN: %target-swift-frontend -typecheck %t/Client_Swift5.swift -I %t \ // RUN: -swift-version 5 \ // RUN: -enable-experimental-feature AccessLevelOnImport \ -// RUN: -package-name pkg -// RUN: %target-swift-frontend -typecheck %t/Client_Swift6.swift -I %t \ +// RUN: -package-name pkg -library-level api +// RUN: %target-swift-frontend -typecheck %t/Client_Swift7.swift -I %t \ // RUN: -enable-upcoming-feature InternalImportsByDefault \ -// RUN: -package-name pkg +// RUN: -package-name pkg -library-level api // REQUIRES: swift_feature_AccessLevelOnImport // REQUIRES: swift_feature_InternalImportsByDefault @@ -53,10 +54,10 @@ internal import InternalLib fileprivate import FileprivateLib private import PrivateLib -//--- Client_Swift6.swift +//--- Client_Swift7.swift import DefaultLib -public import PublicLib // expected-error {{module 'PublicLib' was not compiled with library evolution support; using it means binary compatibility for 'Client_Swift6' can't be guaranteed}} {{1-8=}} +public import PublicLib // expected-error {{module 'PublicLib' was not compiled with library evolution support; using it means binary compatibility for 'Client_Swift7' can't be guaranteed}} {{1-8=}} // expected-warning @-1 {{public import of 'PublicLib' was not used in public declarations or inlinable code}} package import PackageLib // expected-warning @-1 {{package import of 'PackageLib' was not used in package declarations}}