From 1b086e1bfc7d6719ff75cfadc9f1ee21586813d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Fri, 26 Sep 2025 14:14:16 -0700 Subject: [PATCH 1/7] Sema: Embedded functions are fragile unless marked otherwise Consider functions in embedded mode to be fragile unless marked with `@_neverEmitIntoClient`. Basically treating them as if they were `@_alwaysEmitIntoClient` by default. A fragile function cannot reference restricted imports such as `@_implementationOnly`, `internal import`, etc. We consider them as fragile only for type-checking, at the SIL level they remain treated as normal functions like before. This allows the later optimization to work as expected. We may want to likely align both ends of the compiler once this is properly supported in sema. This is enabled only in embedded. Fragile functions in library-evolution are marked with attributes and are already checked. We do not need this is non-embedded non-library-evolution as CMO handles inlining and already takes into consideration `@_implementationOnly` imports. Note: We'll need a similar check for memory layouts exposed to clients. We can still see compiler crashes and miscompiles in this scenario. This will apply to both embedded and non-library-evolution modes. That will likely need to extend the `@_neverEmitIntoClient` attribute and may require a staged deployment. rdar://161365361 --- include/swift/AST/DeclContext.h | 3 +- include/swift/AST/DiagnosticsSema.def | 3 +- lib/AST/DeclContext.cpp | 29 ++- lib/AST/TypeCheckRequests.cpp | 3 + lib/Sema/ResilienceDiagnostics.cpp | 9 +- test/Sema/access-level-import-embedded.swift | 181 ++++++++++++++++++ .../implementation-only-import-embedded.swift | 171 +++++++++++++++++ .../implementation-only-import-build.swift | 59 ++++++ .../linkage/implementation_only_hiding.swift | 32 ++-- 9 files changed, 466 insertions(+), 24 deletions(-) create mode 100644 test/Sema/access-level-import-embedded.swift create mode 100644 test/Sema/implementation-only-import-embedded.swift create mode 100644 test/embedded/implementation-only-import-build.swift diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index b064f05cfd95d..5076540e8e0a3 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -202,7 +202,7 @@ struct ConformanceDiagnostic { ProtocolDecl *ExistingExplicitProtocol; }; -/// Used in diagnostic %selects. +/// Used in diagnostic %selects via FRAGILE_FUNC_KIND. struct FragileFunctionKind { enum Kind : unsigned { Transparent, @@ -211,6 +211,7 @@ struct FragileFunctionKind { DefaultArgument, PropertyInitializer, BackDeploy, + EmbeddedAlwaysEmitIntoClient, None }; diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 9608755f57f0f..0a767cf53a1b0 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -7312,7 +7312,8 @@ ERROR(usable_from_inline_attr_in_protocol,none, "an '@_alwaysEmitIntoClient' function|" \ "a default argument value|" \ "a property initializer in a '@frozen' type|" \ - "a '@backDeployed' function'}" + "a '@backDeployed' function|" \ + "an embedded function not marked '@_neverEmitIntoClient'}" ERROR(local_type_in_inlinable_function, none, "type %0 cannot be nested inside " FRAGILE_FUNC_KIND "1", diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index 86890830c50d3..56dbbe4cf5678 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -488,6 +488,11 @@ ResilienceExpansion DeclContext::getResilienceExpansion() const { case FragileFunctionKind::PropertyInitializer: case FragileFunctionKind::BackDeploy: return ResilienceExpansion::Minimal; + + /// Embedded functions are treated as fragile for diagnostics only. + /// For code gen they are treated as normal and optimized later. + case FragileFunctionKind::EmbeddedAlwaysEmitIntoClient: + case FragileFunctionKind::None: return ResilienceExpansion::Maximal; } @@ -551,6 +556,23 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator, if (AFD->getDeclContext()->isLocalContext()) continue; + // Delay checking the implicit conditions after explicit declarations. + auto checkEmbeddedAlwaysEmitIntoClient = [&](const ValueDecl *VD) { + if (!VD->getASTContext().LangOpts.hasFeature(Feature::Embedded)) + return FragileFunctionKind::None; + + bool funcIsNEIC = VD->isNeverEmittedIntoClient(); + bool storageIsNEIC = false; + if (auto accessor = dyn_cast(VD)) + storageIsNEIC = accessor->getStorage()->isNeverEmittedIntoClient(); + + // Accessors are implicitly EmbeddedAlwaysEmitIntoClient if neither the + // accessor or starage is marked @_neverEmitIntoClient. + if (!funcIsNEIC && !storageIsNEIC) + return FragileFunctionKind::EmbeddedAlwaysEmitIntoClient; + return FragileFunctionKind::None; + }; + auto funcAccess = AFD->getFormalAccessScope(/*useDC=*/nullptr, /*treatUsableFromInlineAsPublic=*/true); @@ -558,7 +580,8 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator, // If the function is not externally visible, we will not be serializing // its body. if (!funcAccess.isPublic()) { - return {FragileFunctionKind::None}; + // For non-public decls, only check embedded mode correctness. + return {checkEmbeddedAlwaysEmitIntoClient(AFD)}; } // If the function is public, @_transparent implies @inlinable. @@ -592,6 +615,10 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator, return {FragileFunctionKind::BackDeploy}; } } + + auto implicitKind = checkEmbeddedAlwaysEmitIntoClient(AFD); + if (implicitKind != FragileFunctionKind::None) + return {implicitKind}; } } diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index a4debbb322221..1cb7cd59a79c3 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -687,6 +687,9 @@ void swift::simple_display(llvm::raw_ostream &out, case FragileFunctionKind::BackDeploy: out << "backDeploy"; return; + case FragileFunctionKind::EmbeddedAlwaysEmitIntoClient: + out << "embeddedAlwaysEmitIntoClient"; + return; case FragileFunctionKind::None: out << "none"; return; diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index 3a4dbd1112107..4b18f10da903d 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -97,6 +97,14 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, return false; } + // Embedded functions can reference non-public decls as they are visible + // to clients. Still report references to decls imported non-publicly + // to enforce access-level on imports. + ImportAccessLevel problematicImport = D->getImportAccessFrom(DC); + if (fragileKind.kind == FragileFunctionKind::EmbeddedAlwaysEmitIntoClient && + !problematicImport) + return false; + DowngradeToWarning downgradeToWarning = DowngradeToWarning::No; // Swift 4.2 did not perform any checks for type aliases. @@ -127,7 +135,6 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, Context.Diags.diagnose(D, diag::resilience_decl_declared_here, D); - ImportAccessLevel problematicImport = D->getImportAccessFrom(DC); if (problematicImport.has_value() && problematicImport->accessLevel < D->getFormalAccess()) { Context.Diags.diagnose(problematicImport->importLoc, diff --git a/test/Sema/access-level-import-embedded.swift b/test/Sema/access-level-import-embedded.swift new file mode 100644 index 0000000000000..25421b4eaf7ee --- /dev/null +++ b/test/Sema/access-level-import-embedded.swift @@ -0,0 +1,181 @@ +/// Test @_implementationOnly internal import exportability diagnostics in embedded mode. + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -o %t/indirects.swiftmodule \ +// RUN: %S/Inputs/implementation-only-imports/indirects.swift \ +// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -enable-experimental-feature Embedded +// RUN: %target-swift-frontend -emit-module -o %t/directs.swiftmodule -I %t \ +// RUN: %S/Inputs/implementation-only-imports/directs.swift \ +// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -enable-experimental-feature Embedded + +// RUN: %target-swift-frontend -typecheck -verify %s -I %t \ +// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -enable-experimental-feature Embedded + +// REQUIRES: swift_feature_Embedded + +@_implementationOnly internal import directs +// expected-warning @-1 {{using '@_implementationOnly' without enabling library evolution for 'main' may lead to instability during execution}} +// expected-note @-2 19 {{struct 'StructFromDirect' imported as 'internal' from 'directs' here}} +// expected-note @-3 12 {{initializer 'init()' imported as 'internal' from 'directs' here}} +import indirects + +internal func localInternalFunc() {} // expected-note {{global function 'localInternalFunc()' is not '@usableFromInline' or public}} + +@inlinable +public func explicitlyInlinable(arg: StructFromDirect = StructFromDirect()) { +// expected-error @-1 {{initializer 'init()' is internal and cannot be referenced from a default argument value}} +// expected-error @-2 {{struct 'StructFromDirect' is internal and cannot be referenced from a default argument value}} +// expected-error @-3 {{struct 'StructFromDirect' is internal and cannot be referenced from an '@inlinable' function}} +// expected-error @-4 {{function cannot be declared public because its parameter uses an internal type}} +// expected-note @-5 {{struct 'StructFromDirect' is imported by this file as 'internal' from 'directs'}} + _ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an '@inlinable' function}} + // expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an '@inlinable' function}} + + if (true) { + _ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an '@inlinable' function}} + // expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an '@inlinable' function}} + } + + func nested() { + _ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an '@inlinable' function}} + // expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an '@inlinable' function}} + } + nested() + + localInternalFunc() // expected-error {{global function 'localInternalFunc()' is internal and cannot be referenced from an '@inlinable' function}} + + explicitlyInlinable() + implicitlyInlinablePublic() + implicitlyInlinablePrivate() // expected-error {{global function 'implicitlyInlinablePrivate(arg:)' is private and cannot be referenced from an '@inlinable' function}} + explicitNonInliable() +} + +public func implicitlyInlinablePublic(arg: StructFromDirect = StructFromDirect()) { +// expected-error @-1 {{initializer 'init()' is internal and cannot be referenced from a default argument value}} +// expected-error @-2 {{struct 'StructFromDirect' is internal and cannot be referenced from a default argument value}} +// expected-error @-3 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}} +// expected-error @-4 {{function cannot be declared public because its parameter uses an internal type}} +// expected-note @-5 {{struct 'StructFromDirect' is imported by this file as 'internal' from 'directs'}} + _ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}} + // expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}} + + if (true) { + _ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}} + // expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}} + } + + func nested() { + _ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}} + // expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}} + } + nested() + + localInternalFunc() + + explicitlyInlinable() + implicitlyInlinablePublic() + implicitlyInlinablePrivate() + explicitNonInliable() +} + +private func implicitlyInlinablePrivate(arg: StructFromDirect = StructFromDirect()) { +// expected-error @-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}} +// expected-note @-2 {{global function 'implicitlyInlinablePrivate(arg:)' is not '@usableFromInline' or public}} + _ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}} + // expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}} + + if (true) { + _ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}} + // expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}} + } + + func nested() { + _ = StructFromDirect() // expected-error {{initializer 'init()' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}} + // expected-error@-1 {{struct 'StructFromDirect' is internal and cannot be referenced from an embedded function not marked '@_neverEmitIntoClient'}} + } + nested() + + localInternalFunc() + + explicitlyInlinable() + implicitlyInlinablePublic() + implicitlyInlinablePrivate() + explicitNonInliable() +} + +@_neverEmitIntoClient +public func explicitNonInliable(arg: StructFromDirect = StructFromDirect()) { +// expected-error @-1 {{initializer 'init()' is internal and cannot be referenced from a default argument value}} +// expected-error @-2 {{struct 'StructFromDirect' is internal and cannot be referenced from a default argument value}} +// expected-error @-3 {{cannot use struct 'StructFromDirect' here; 'directs' has been imported as implementation-only}} +// expected-error @-4 {{function cannot be declared public because its parameter uses an internal type}} +// expected-note @-5 {{struct 'StructFromDirect' is imported by this file as 'internal' from 'directs'}} + _ = StructFromDirect() + + if (true) { + _ = StructFromDirect() + } + + @_neverEmitIntoClient + func nested() { + _ = StructFromDirect() + } + nested() + + localInternalFunc() + + explicitlyInlinable() + implicitlyInlinablePublic() + implicitlyInlinablePrivate() + explicitNonInliable() +} + +@_neverEmitIntoClient +internal func explicitNonInliableInternal(arg: StructFromDirect = StructFromDirect()) { + _ = StructFromDirect() + + if (true) { + _ = StructFromDirect() + } + + @_neverEmitIntoClient + func nested() { + _ = StructFromDirect() + } + nested() + + localInternalFunc() + + explicitlyInlinable() + implicitlyInlinablePublic() + implicitlyInlinablePrivate() + explicitNonInliable() +} + +public func legalAccessToIndirect(arg: StructFromIndirect = StructFromIndirect()) { + _ = StructFromIndirect() + + if (true) { + _ = StructFromIndirect() + } + + func nested() { + _ = StructFromIndirect() + } + nested() +} + +public struct ExposedLayoutPublic { + public var publicField: StructFromDirect // expected-error {{property cannot be declared public because its type uses an internal type}} + // expected-error @-1 {{cannot use struct 'StructFromDirect' here; 'directs' has been imported as implementation-only}} + // expected-note @-2 {{struct 'StructFromDirect' is imported by this file as 'internal' from 'directs'}} + + private var privateField: StructFromDirect +} + +private struct ExposedLayoutPrivate { + private var privateField: StructFromDirect +} diff --git a/test/Sema/implementation-only-import-embedded.swift b/test/Sema/implementation-only-import-embedded.swift new file mode 100644 index 0000000000000..2daa68a55d769 --- /dev/null +++ b/test/Sema/implementation-only-import-embedded.swift @@ -0,0 +1,171 @@ +/// Test @_implementationOnly import exportability diagnostics in embedded mode. + +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -o %t/indirects.swiftmodule \ +// RUN: %S/Inputs/implementation-only-imports/indirects.swift \ +// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -enable-experimental-feature Embedded +// RUN: %target-swift-frontend -emit-module -o %t/directs.swiftmodule -I %t\ +// RUN: %S/Inputs/implementation-only-imports/directs.swift \ +// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -enable-experimental-feature Embedded + +// RUN: %target-swift-frontend -typecheck -verify %s -I %t \ +// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -enable-experimental-feature Embedded + +// REQUIRES: swift_feature_Embedded + +@_implementationOnly import directs +// expected-warning @-1 {{using '@_implementationOnly' without enabling library evolution for 'main' may lead to instability during execution}} +import indirects + +internal func localInternalFunc() {} // expected-note {{global function 'localInternalFunc()' is not '@usableFromInline' or public}} + +@inlinable +public func explicitlyInlinable(arg: StructFromDirect = StructFromDirect()) { +// expected-error @-1 {{initializer 'init()' cannot be used in a default argument value because 'directs' was imported implementation-only}} +// expected-error @-2 {{struct 'StructFromDirect' cannot be used in a default argument value because 'directs' was imported implementation-only}} +// expected-error @-3 {{struct 'StructFromDirect' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} + _ = StructFromDirect() // expected-error {{initializer 'init()' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} + // expected-error@-1 {{struct 'StructFromDirect' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} + + if (true) { + _ = StructFromDirect() // expected-error {{initializer 'init()' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} + // expected-error@-1 {{struct 'StructFromDirect' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} + } + + func nested() { + _ = StructFromDirect() // expected-error {{initializer 'init()' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} + // expected-error@-1 {{struct 'StructFromDirect' cannot be used in an '@inlinable' function because 'directs' was imported implementation-only}} + } + nested() + + localInternalFunc() // expected-error {{global function 'localInternalFunc()' is internal and cannot be referenced from an '@inlinable' function}} + explicitlyInlinable() + implicitlyInlinablePublic() + implicitlyInlinablePrivate() // expected-error {{global function 'implicitlyInlinablePrivate(arg:)' is private and cannot be referenced from an '@inlinable' function}} + explicitNonInliable() +} + +public func implicitlyInlinablePublic(arg: StructFromDirect = StructFromDirect()) { +// expected-error @-1 {{initializer 'init()' cannot be used in a default argument value because 'directs' was imported implementation-only}} +// expected-error @-2 {{struct 'StructFromDirect' cannot be used in a default argument value because 'directs' was imported implementation-only}} +// expected-error @-3 {{struct 'StructFromDirect' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + + _ = StructFromDirect() // expected-error {{initializer 'init()' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + // expected-error@-1 {{struct 'StructFromDirect' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + + if (true) { + _ = StructFromDirect() // expected-error {{initializer 'init()' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + // expected-error@-1 {{struct 'StructFromDirect' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + } + + func nested() { + _ = StructFromDirect() // expected-error {{initializer 'init()' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + // expected-error@-1 {{struct 'StructFromDirect' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + } + nested() + + localInternalFunc() + + explicitlyInlinable() + implicitlyInlinablePublic() + implicitlyInlinablePrivate() + explicitNonInliable() +} + +private func implicitlyInlinablePrivate(arg: StructFromDirect = StructFromDirect()) { +// expected-error @-1 {{struct 'StructFromDirect' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} +// expected-note @-2 {{global function 'implicitlyInlinablePrivate(arg:)' is not '@usableFromInline' or public}} + + _ = StructFromDirect() // expected-error {{initializer 'init()' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + // expected-error@-1 {{struct 'StructFromDirect' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + + if (true) { + _ = StructFromDirect() // expected-error {{initializer 'init()' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + // expected-error@-1 {{struct 'StructFromDirect' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + } + + func nested() { + _ = StructFromDirect() // expected-error {{initializer 'init()' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + // expected-error@-1 {{struct 'StructFromDirect' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + } + nested() + + localInternalFunc() + + explicitlyInlinable() + implicitlyInlinablePublic() + implicitlyInlinablePrivate() + explicitNonInliable() +} + +@_neverEmitIntoClient +public func explicitNonInliable(arg: StructFromDirect = StructFromDirect()) { +// expected-error @-1 {{initializer 'init()' cannot be used in a default argument value because 'directs' was imported implementation-only}} +// expected-error @-2 {{struct 'StructFromDirect' cannot be used in a default argument value because 'directs' was imported implementation-only}} +// expected-error @-3 {{cannot use struct 'StructFromDirect' here; 'directs' has been imported as implementation-only}} + + _ = StructFromDirect() + + if (true) { + _ = StructFromDirect() + } + + func nested() { + _ = StructFromDirect() + } + nested() + + localInternalFunc() + + explicitlyInlinable() + implicitlyInlinablePublic() + implicitlyInlinablePrivate() + explicitNonInliable() +} + +@_neverEmitIntoClient +internal func explicitNonInliableInternal(arg: StructFromDirect = StructFromDirect()) { + _ = StructFromDirect() + + if (true) { + _ = StructFromDirect() + } + + func nested() { + _ = StructFromDirect() + } + nested() + + localInternalFunc() + + explicitlyInlinable() + implicitlyInlinablePublic() + implicitlyInlinablePrivate() + explicitNonInliable() +} + +public func legalAccessToIndirect(arg: StructFromIndirect = StructFromIndirect()) { + _ = StructFromIndirect() + + if (true) { + _ = StructFromIndirect() + } + + func nested() { + _ = StructFromIndirect() + } + nested() +} + +public struct ExposedLayoutPublic { + public var publicField: StructFromDirect // expected-error {{cannot use struct 'StructFromDirect' here; 'directs' has been imported as implementation-only}} + + private var privateField: StructFromDirect // FIXME should error +} + +private struct ExposedLayoutPrivate { + private var privateField: StructFromDirect // FIXME should error +} diff --git a/test/embedded/implementation-only-import-build.swift b/test/embedded/implementation-only-import-build.swift new file mode 100644 index 0000000000000..577ba0b07f0d2 --- /dev/null +++ b/test/embedded/implementation-only-import-build.swift @@ -0,0 +1,59 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -o %t/indirects.swiftmodule \ +// RUN: %S/../Sema/Inputs/implementation-only-imports/indirects.swift \ +// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -enable-experimental-feature Embedded +// RUN: %target-swift-frontend -emit-module -o %t/directs.swiftmodule -I %t\ +// RUN: %S/../Sema/Inputs/implementation-only-imports/directs.swift \ +// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -enable-experimental-feature Embedded + +// RUN: %target-swift-frontend -emit-sil %s -I %t \ +// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -enable-experimental-feature Embedded + +// REQUIRES: swift_feature_Embedded + +@_implementationOnly import directs +import indirects + +internal func localInternalFunc() {} + +public func implicitlyInlinablePublic() { + localInternalFunc() +} + +private func implicitlyInlinablePrivate() { + localInternalFunc() +} + +@_neverEmitIntoClient +public func explicitNonInliable() { + _ = StructFromDirect() + + if (true) { + _ = StructFromDirect() + } + + @_neverEmitIntoClient + func nested() { + _ = StructFromDirect() + } + nested() + + localInternalFunc() +} + +@_alwaysEmitIntoClient +public func legalAccessToIndirect() { + _ = StructFromIndirect() + + if (true) { + _ = StructFromIndirect() + } + + func nested() { + _ = StructFromIndirect() + } + nested() +} diff --git a/test/embedded/linkage/implementation_only_hiding.swift b/test/embedded/linkage/implementation_only_hiding.swift index 079c6f02cc7ca..a987f75925594 100644 --- a/test/embedded/linkage/implementation_only_hiding.swift +++ b/test/embedded/linkage/implementation_only_hiding.swift @@ -12,7 +12,7 @@ // RUN: cp %S/Inputs/CHeader.h %t/Dependencies/ // RUN: cp %S/Inputs/module.modulemap %t/Dependencies/ -// RUN: split-file %s %t/Files +// RUN: split-file %s %t/Files --leading-lines // Compile the Swift dependencies into that same location. // RUN: %target-swift-frontend -parse-as-library -emit-module %t/Dependencies/SwiftDependency.swift -enable-experimental-feature Embedded -o %t/Dependencies/SwiftDependency.swiftmodule @@ -21,6 +21,9 @@ // against the dependencies. // RUN: %target-swift-frontend -parse-as-library -emit-module %t/Files/Library.swift -enable-experimental-feature Embedded -I %t/Dependencies/ -o %t/Modules/Library.swiftmodule +// Building the library with invalid uses trigger errors. +// RUN: %target-swift-frontend -parse-as-library -typecheck -verify %t/Files/Library.swift -enable-experimental-feature Embedded -I %t/Dependencies/ -o %t/Modules/Library.swiftmodule -DBAD_IOI_USAGE + // Remove the dependencies so there is no way we can find them later. // RUN: rm -rf %t/Dependencies @@ -28,19 +31,12 @@ // @_neverEmitIntoClient hides the body of test(). // RUN: %target-swift-frontend -emit-ir -parse-as-library %t/Files/Application.swift -enable-experimental-feature Embedded -I %t/Modules -o %t/Application.ir -// Build the application against the library, but intentionally trigger -// deserialization of some serialized SIL that refers to an implementation-only -// dependency. Right now, these fail spectacularly. Over time, we want them to -// become compile-time errors or start working. -// RUN: not --crash %target-swift-frontend -emit-ir -parse-as-library %t/Files/Application.swift -enable-experimental-feature Embedded -I %t/Modules -o %t/Application.ir -DBAD_C_USAGE -// RUN: not --crash %target-swift-frontend -emit-ir -parse-as-library %t/Files/Application.swift -enable-experimental-feature Embedded -I %t/Modules -o %t/Application.ir -DBAD_SWIFT_USAGE - // REQUIRES: swift_in_compiler // REQUIRES: swift_feature_Embedded //--- Library.swift -@_implementationOnly import CDependency -@_implementationOnly import SwiftDependency +@_implementationOnly import CDependency // expected-warning {{using '@_implementationOnly' without enabling library evolution for 'Library' may lead to instability during execution}} +@_implementationOnly import SwiftDependency // expected-warning {{using '@_implementationOnly' without enabling library evolution for 'Library' may lead to instability during execution}} @_neverEmitIntoClient public func test() { @@ -48,13 +44,17 @@ public func test() { A().doSomething() } +#if BAD_IOI_USAGE public func badCLibraryUsage() { - _ = getPoint(3.14159, 2.71828) + _ = getPoint(3.14159, 2.71828) // expected-error {{global function 'getPoint' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'CDependency' was imported implementation-only}} } public func badSwiftLibraryUsage() { - A().doSomething() + A().doSomething() // expected-error {{struct 'A' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'SwiftDependency' was imported implementation-only}} + // expected-error @-1 {{initializer 'init()' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'SwiftDependency' was imported implementation-only}} + // expected-error @-2 {{instance method 'doSomething()' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'SwiftDependency' was imported implementation-only}} } +#endif //--- Application.swift @@ -62,12 +62,4 @@ import Library public func useTest() { test() - -#if BAD_C_USAGE - badCLibraryUsage() -#endif - -#if BAD_SWIFT_USAGE - badSwiftLibraryUsage() -#endif } From 0136e19daee4982e6dda13e7e0436b96e3fce54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Wed, 1 Oct 2025 13:57:36 -0700 Subject: [PATCH 2/7] Sema: Allow availability macros in embedded code Availability macros are rejected from library-evolution inlinable code as the compiler didn't know how to print them in the swiftinterface. This isn't a concern with the embedded style of fragile functions, we can allow availability macros there. --- lib/Sema/MiscDiagnostics.cpp | 3 ++- test/Sema/implementation-only-import-embedded.swift | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 780ea16a13dc6..5823e41de508a 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -5223,7 +5223,8 @@ static bool diagnoseAvailabilityCondition(PoundAvailableInfo *info, // restriction, macros would need to either be expanded when printed in // swiftinterfaces or be parsable as macros by module clients. auto fragileKind = DC->getFragileFunctionKind(); - if (fragileKind.kind != FragileFunctionKind::None) { + if (fragileKind.kind != FragileFunctionKind::None && + fragileKind.kind != FragileFunctionKind::EmbeddedAlwaysEmitIntoClient) { for (auto availSpec : info->getQueries()) { if (availSpec->getMacroLoc().isValid()) { diags.diagnose(availSpec->getMacroLoc(), diff --git a/test/Sema/implementation-only-import-embedded.swift b/test/Sema/implementation-only-import-embedded.swift index 2daa68a55d769..99a9998c22e12 100644 --- a/test/Sema/implementation-only-import-embedded.swift +++ b/test/Sema/implementation-only-import-embedded.swift @@ -12,6 +12,7 @@ // RUN: %target-swift-frontend -typecheck -verify %s -I %t \ // RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -define-availability "availMacro:macOS 26.0, iOS 26.0" \ // RUN: -enable-experimental-feature Embedded // REQUIRES: swift_feature_Embedded @@ -73,6 +74,8 @@ public func implicitlyInlinablePublic(arg: StructFromDirect = StructFromDirect() implicitlyInlinablePublic() implicitlyInlinablePrivate() explicitNonInliable() + + if #available(availMacro, *) { } } private func implicitlyInlinablePrivate(arg: StructFromDirect = StructFromDirect()) { From 495035d61282676cee132a2b2abc0d46dd23a799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 2 Oct 2025 11:21:39 -0700 Subject: [PATCH 3/7] Sema: Add tests for embedded exportability checking with accessors --- .../implementation-only-import-embedded.swift | 33 +++++++++++++++++++ .../implementation-only-import-build.swift | 9 +++++ 2 files changed, 42 insertions(+) diff --git a/test/Sema/implementation-only-import-embedded.swift b/test/Sema/implementation-only-import-embedded.swift index 99a9998c22e12..b3d53ce618bc7 100644 --- a/test/Sema/implementation-only-import-embedded.swift +++ b/test/Sema/implementation-only-import-embedded.swift @@ -150,6 +150,39 @@ internal func explicitNonInliableInternal(arg: StructFromDirect = StructFromDire explicitNonInliable() } +struct Accessors { + public var var1: Int { + get { + globalFunctionFromDirect() // expected-error {{global function 'globalFunctionFromDirect()' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + return 0 + } + } + + @_alwaysEmitIntoClient + public var var2: Int { + get { + globalFunctionFromDirect() // expected-error {{global function 'globalFunctionFromDirect()' cannot be used in an embedded function not marked '@_neverEmitIntoClient' because 'directs' was imported implementation-only}} + return 0 + } + } + + @_neverEmitIntoClient + public var var3: Int { + get { + globalFunctionFromDirect() + return 0 + } + } + + public var var4: Int { + @_neverEmitIntoClient + get { + globalFunctionFromDirect() + return 0 + } + } +} + public func legalAccessToIndirect(arg: StructFromIndirect = StructFromIndirect()) { _ = StructFromIndirect() diff --git a/test/embedded/implementation-only-import-build.swift b/test/embedded/implementation-only-import-build.swift index 577ba0b07f0d2..3f7994e903eae 100644 --- a/test/embedded/implementation-only-import-build.swift +++ b/test/embedded/implementation-only-import-build.swift @@ -57,3 +57,12 @@ public func legalAccessToIndirect() { } nested() } + +extension Array { + @_alwaysEmitIntoClient + public var myMutableSpan: Int { + get { + return 0 + } + } +} From efbc9386942f02af8a636f56892ccc8f587f9430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 2 Oct 2025 15:58:17 -0700 Subject: [PATCH 4/7] Sema: Allow nested structs in embedded code --- lib/Sema/TypeCheckDeclPrimary.cpp | 3 ++- test/Sema/implementation-only-import-embedded.swift | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 101d711135eda..9e41ebf8fa5dd 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -2946,7 +2946,8 @@ class DeclChecker : public DeclVisitor { // We don't allow nested types inside inlinable contexts. auto kind = DC->getFragileFunctionKind(); - if (kind.kind != FragileFunctionKind::None) { + if (kind.kind != FragileFunctionKind::None && + kind.kind != FragileFunctionKind::EmbeddedAlwaysEmitIntoClient) { NTD->diagnose(diag::local_type_in_inlinable_function, NTD->getName(), kind.getSelector()); } diff --git a/test/Sema/implementation-only-import-embedded.swift b/test/Sema/implementation-only-import-embedded.swift index b3d53ce618bc7..995d9c5292321 100644 --- a/test/Sema/implementation-only-import-embedded.swift +++ b/test/Sema/implementation-only-import-embedded.swift @@ -148,6 +148,8 @@ internal func explicitNonInliableInternal(arg: StructFromDirect = StructFromDire implicitlyInlinablePublic() implicitlyInlinablePrivate() explicitNonInliable() + + struct NestedStruct {} } struct Accessors { From f758e210f1a05c515bf79c5cfb96cdc59a3c8983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 2 Oct 2025 16:42:26 -0700 Subject: [PATCH 5/7] Sema: Add requires to IOI + embedded code using the stdlib --- test/Sema/access-level-import-embedded.swift | 7 ++++--- test/Sema/implementation-only-import-embedded.swift | 7 ++++--- test/embedded/implementation-only-import-build.swift | 7 ++++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/test/Sema/access-level-import-embedded.swift b/test/Sema/access-level-import-embedded.swift index 25421b4eaf7ee..667d081cbc612 100644 --- a/test/Sema/access-level-import-embedded.swift +++ b/test/Sema/access-level-import-embedded.swift @@ -3,18 +3,19 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -o %t/indirects.swiftmodule \ // RUN: %S/Inputs/implementation-only-imports/indirects.swift \ -// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded // RUN: %target-swift-frontend -emit-module -o %t/directs.swiftmodule -I %t \ // RUN: %S/Inputs/implementation-only-imports/directs.swift \ -// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded // RUN: %target-swift-frontend -typecheck -verify %s -I %t \ -// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded // REQUIRES: swift_feature_Embedded +// REQUIRES: embedded_stdlib_cross_compiling @_implementationOnly internal import directs // expected-warning @-1 {{using '@_implementationOnly' without enabling library evolution for 'main' may lead to instability during execution}} diff --git a/test/Sema/implementation-only-import-embedded.swift b/test/Sema/implementation-only-import-embedded.swift index 995d9c5292321..88f35c55e20b0 100644 --- a/test/Sema/implementation-only-import-embedded.swift +++ b/test/Sema/implementation-only-import-embedded.swift @@ -3,19 +3,20 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -o %t/indirects.swiftmodule \ // RUN: %S/Inputs/implementation-only-imports/indirects.swift \ -// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded // RUN: %target-swift-frontend -emit-module -o %t/directs.swiftmodule -I %t\ // RUN: %S/Inputs/implementation-only-imports/directs.swift \ -// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded // RUN: %target-swift-frontend -typecheck -verify %s -I %t \ -// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -define-availability "availMacro:macOS 26.0, iOS 26.0" \ // RUN: -enable-experimental-feature Embedded // REQUIRES: swift_feature_Embedded +// REQUIRES: embedded_stdlib_cross_compiling @_implementationOnly import directs // expected-warning @-1 {{using '@_implementationOnly' without enabling library evolution for 'main' may lead to instability during execution}} diff --git a/test/embedded/implementation-only-import-build.swift b/test/embedded/implementation-only-import-build.swift index 3f7994e903eae..0ffdd7ad480ca 100644 --- a/test/embedded/implementation-only-import-build.swift +++ b/test/embedded/implementation-only-import-build.swift @@ -1,18 +1,19 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -o %t/indirects.swiftmodule \ // RUN: %S/../Sema/Inputs/implementation-only-imports/indirects.swift \ -// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded // RUN: %target-swift-frontend -emit-module -o %t/directs.swiftmodule -I %t\ // RUN: %S/../Sema/Inputs/implementation-only-imports/directs.swift \ -// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded // RUN: %target-swift-frontend -emit-sil %s -I %t \ -// RUN: -swift-version 5 -target arm64-apple-macosx15.0 \ +// RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded // REQUIRES: swift_feature_Embedded +// REQUIRES: embedded_stdlib_cross_compiling @_implementationOnly import directs import indirects From dd4221d2bd322c90f6232c68542dbe8a282f5e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Mon, 6 Oct 2025 10:28:31 -0700 Subject: [PATCH 6/7] Embedded: Allow implementation-only-import-build to build wider --- test/embedded/implementation-only-import-build.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/embedded/implementation-only-import-build.swift b/test/embedded/implementation-only-import-build.swift index 0ffdd7ad480ca..82b0b276deb03 100644 --- a/test/embedded/implementation-only-import-build.swift +++ b/test/embedded/implementation-only-import-build.swift @@ -1,15 +1,12 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -o %t/indirects.swiftmodule \ // RUN: %S/../Sema/Inputs/implementation-only-imports/indirects.swift \ -// RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded // RUN: %target-swift-frontend -emit-module -o %t/directs.swiftmodule -I %t\ // RUN: %S/../Sema/Inputs/implementation-only-imports/directs.swift \ -// RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded // RUN: %target-swift-frontend -emit-sil %s -I %t \ -// RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded // REQUIRES: swift_feature_Embedded From 938050720e102414dca49bf868079b8c77b6ff3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Tue, 14 Oct 2025 13:59:09 -0700 Subject: [PATCH 7/7] Sema: Use -verify-ignore-unrelated in tests --- test/Sema/access-level-import-embedded.swift | 2 +- test/Sema/implementation-only-import-embedded.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Sema/access-level-import-embedded.swift b/test/Sema/access-level-import-embedded.swift index 667d081cbc612..b81ac9a7db56f 100644 --- a/test/Sema/access-level-import-embedded.swift +++ b/test/Sema/access-level-import-embedded.swift @@ -10,7 +10,7 @@ // RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded -// RUN: %target-swift-frontend -typecheck -verify %s -I %t \ +// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unrelated %s -I %t \ // RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded diff --git a/test/Sema/implementation-only-import-embedded.swift b/test/Sema/implementation-only-import-embedded.swift index 88f35c55e20b0..831f44f946d2a 100644 --- a/test/Sema/implementation-only-import-embedded.swift +++ b/test/Sema/implementation-only-import-embedded.swift @@ -10,7 +10,7 @@ // RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -enable-experimental-feature Embedded -// RUN: %target-swift-frontend -typecheck -verify %s -I %t \ +// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unrelated %s -I %t \ // RUN: -swift-version 5 -target arm64-apple-none-macho \ // RUN: -define-availability "availMacro:macOS 26.0, iOS 26.0" \ // RUN: -enable-experimental-feature Embedded