diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 68da47b4154..31436b867c6 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3324,6 +3324,9 @@ public: }; } + /// Should the underlying type be visible to clients outside of the module? + bool exportUnderlyingType() const; + /// The substitutions that map the generic parameters of the opaque type to /// the unique underlying types, when that information is known. llvm::Optional getUniqueUnderlyingTypeSubstitutions() const { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 2766f9bc8ae..15b2054f7a6 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -9547,6 +9547,25 @@ GenericTypeParamDecl *OpaqueTypeDecl::getExplicitGenericParam( return genericParamType->getDecl(); } +bool OpaqueTypeDecl::exportUnderlyingType() const { + auto mod = getDeclContext()->getParentModule(); + if (mod->getResilienceStrategy() != ResilienceStrategy::Resilient) + return true; + + ValueDecl *namingDecl = getNamingDecl(); + if (auto *AFD = dyn_cast(namingDecl)) + return AFD->getResilienceExpansion() == ResilienceExpansion::Minimal; + + if (auto *ASD = dyn_cast(namingDecl)) { + for (auto *accessor : ASD->getAllAccessors()) + if (accessor->getResilienceExpansion() == ResilienceExpansion::Minimal) + return true; + return false; + } + + llvm_unreachable("The naming decl is expected to be either an AFD or ASD"); +} + llvm::Optional OpaqueTypeDecl::getAnonymousOpaqueParamOrdinal(TypeRepr *repr) const { assert(NamingDeclAndHasOpaqueReturnTypeRepr.getInt() && diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 5a98e861417..46336f332e9 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -4221,11 +4221,13 @@ public: GenericSignatureID genericSigID; SubstitutionMapID underlyingTypeSubsID; uint8_t rawAccessLevel; + bool exportUnderlyingType; decls_block::OpaqueTypeLayout::readRecord(scratch, contextID, namingDeclID, interfaceSigID, interfaceTypeID, genericSigID, underlyingTypeSubsID, - rawAccessLevel); + rawAccessLevel, + exportUnderlyingType); auto declContext = MF.getDeclContext(contextID); auto interfaceSigOrErr = MF.getGenericSignatureChecked(interfaceSigID); @@ -4261,16 +4263,25 @@ public: else opaqueDecl->setGenericSignature(GenericSignature()); - auto *AFD = dyn_cast(namingDecl); - if (MF.getResilienceStrategy() == ResilienceStrategy::Resilient && - !MF.FileContext->getParentModule()->isMainModule() && - AFD && AFD->getResilienceExpansion() != ResilienceExpansion::Minimal) { + if (!MF.FileContext->getParentModule()->isMainModule() && + !exportUnderlyingType) { // Do not try to read the underlying type information if the function // is not inlinable in clients. This reflects the swiftinterface behavior // in where clients are only aware of the underlying type when the body // of the function is public. + LLVM_DEBUG( + llvm::dbgs() << "Ignoring underlying information for opaque type of '"; + llvm::dbgs() << namingDecl->getName(); + llvm::dbgs() << "'\n"; + ); } else if (underlyingTypeSubsID) { + LLVM_DEBUG( + llvm::dbgs() << "Loading underlying information for opaque type of '"; + llvm::dbgs() << namingDecl->getName(); + llvm::dbgs() << "'\n"; + ); + auto subMapOrError = MF.getSubstitutionMapChecked(underlyingTypeSubsID); if (!subMapOrError) { // If the underlying type references internal details, ignore it. diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 6770d43beb4..c9f434bcd19 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 816; // throw_addr +const uint16_t SWIFTMODULE_VERSION_MINOR = 817; // Opaque type export details /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1641,7 +1641,8 @@ namespace decls_block { TypeIDField, // interface type for opaque type GenericSignatureIDField, // generic environment SubstitutionMapIDField, // optional substitution map for underlying type - AccessLevelField // access level + AccessLevelField, // access level + BCFixed<1> // export underlying type details // trailed by generic parameters // trailed by conditional substitutions >; diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 103626be3a5..148b342cacf 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -4490,11 +4490,14 @@ public: } uint8_t rawAccessLevel = getRawStableAccessLevel(opaqueDecl->getFormalAccess()); + bool exportDetails = opaqueDecl->exportUnderlyingType(); + unsigned abbrCode = S.DeclTypeAbbrCodes[OpaqueTypeLayout::Code]; OpaqueTypeLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, contextID.getOpaqueValue(), namingDeclID, interfaceSigID, interfaceTypeID, genericSigID, - underlyingSubsID, rawAccessLevel); + underlyingSubsID, rawAccessLevel, + exportDetails); writeGenericParams(opaqueDecl->getGenericParams()); // Serialize all of the conditionally available substitutions expect the diff --git a/test/Serialization/ignore-opaque-underlying-type-back-deploy.swift b/test/Serialization/ignore-opaque-underlying-type-back-deploy.swift new file mode 100644 index 00000000000..6f7563f1b9a --- /dev/null +++ b/test/Serialization/ignore-opaque-underlying-type-back-deploy.swift @@ -0,0 +1,70 @@ +/// Variant of ignore-opaque-underlying-type because of the macOS host need. +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +// REQUIRES: asserts +// REQUIRES: OS=macosx + +/// Resilient scenario, we ignore underlying type of non-inlinable functions. +/// Build libraries. +// RUN: %target-swift-frontend -emit-module %t/Lib.swift \ +// RUN: -swift-version 5 -enable-library-evolution \ +// RUN: -emit-module-path %t/Lib.swiftmodule \ +// RUN: -emit-module-interface-path %t/Lib.swiftinterface -verify + +/// Build clients, with and without safety. +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -verify -Xllvm -debug-only=Serialization \ +// RUN: -enable-deserialization-safety 2>&1 \ +// RUN: | %FileCheck %s + +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -verify -Xllvm -debug-only=Serialization \ +// RUN: -disable-deserialization-safety 2>&1 \ +// RUN: | %FileCheck %s + +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -verify -Xllvm -debug-only=Serialization \ +// RUN: -disable-access-control 2>&1 \ +// RUN: | %FileCheck %s + +/// Build against the swiftinterface. +// RUN: rm %t/Lib.swiftmodule +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -verify -Xllvm -debug-only=Serialization \ +// RUN: -disable-deserialization-safety 2>&1 \ +// RUN: | %FileCheck %s + +/// Non-resilient scenario, all underlying types are loaded. +// RUN: %empty-directory(%t) +// RUN: split-file %s %t +// RUN: %target-swift-frontend -emit-module %t/Lib.swift \ +// RUN: -swift-version 5 \ +// RUN: -emit-module-path %t/Lib.swiftmodule -verify +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -verify -Xllvm -debug-only=Serialization \ +// RUN: -disable-deserialization-safety 2>&1 \ +// RUN: | %FileCheck --check-prefix=NON-RESILIENT %s +// NON-RESILIENT-NOT: Ignoring underlying information + +//--- Lib.swift +public protocol V {} + +public struct EV : V { + public init () {} +} + +@available(SwiftStdlib 5.1, *) +public extension V { +// CHECK: Loading underlying information for opaque type of 'backdeployedOpaqueFunc()' + @backDeployed(before: SwiftStdlib 5.1) // expected-warning 4 {{'@backDeployed' is unsupported on a instance method with a 'some' return type}} + func backdeployedOpaqueFunc() -> some V { EV() } +} + +//--- Client.swift +import Lib + +if #available(SwiftStdlib 5.1, *) { + let v = EV() + let _ = v.backdeployedOpaqueFunc() +} diff --git a/test/Serialization/ignore-opaque-underlying-type.swift b/test/Serialization/ignore-opaque-underlying-type.swift new file mode 100644 index 00000000000..19a4c7a1f27 --- /dev/null +++ b/test/Serialization/ignore-opaque-underlying-type.swift @@ -0,0 +1,122 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +// REQUIRES: asserts + +/// Resilient scenario, we ignore underlying type of non-inlinable functions. +/// Build libraries. +// RUN: %target-swift-frontend -emit-module %t/Lib.swift \ +// RUN: -swift-version 5 -enable-library-evolution \ +// RUN: -emit-module-path %t/Lib.swiftmodule \ +// RUN: -emit-module-interface-path %t/Lib.swiftinterface -verify + +/// Build clients, with and without safety. +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -verify -Xllvm -debug-only=Serialization \ +// RUN: -enable-deserialization-safety 2>&1 \ +// RUN: | %FileCheck %s + +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -verify -Xllvm -debug-only=Serialization \ +// RUN: -disable-deserialization-safety 2>&1 \ +// RUN: | %FileCheck %s + +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -verify -Xllvm -debug-only=Serialization \ +// RUN: -disable-access-control 2>&1 \ +// RUN: | %FileCheck %s + +/// Build against the swiftinterface. +// RUN: rm %t/Lib.swiftmodule +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -verify -Xllvm -debug-only=Serialization \ +// RUN: -disable-deserialization-safety 2>&1 \ +// RUN: | %FileCheck %s + +/// Non-resilient scenario, all underlying types are loaded. +// RUN: %empty-directory(%t) +// RUN: split-file %s %t +// RUN: %target-swift-frontend -emit-module %t/Lib.swift \ +// RUN: -swift-version 5 \ +// RUN: -emit-module-path %t/Lib.swiftmodule -verify +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -verify -Xllvm -debug-only=Serialization \ +// RUN: -disable-deserialization-safety 2>&1 \ +// RUN: | %FileCheck --check-prefix=NON-RESILIENT %s +// NON-RESILIENT-NOT: Ignoring underlying information + +//--- Lib.swift +public protocol V {} + +public struct EV : V { + public init () {} +} + +@available(SwiftStdlib 5.1, *) +public extension V { + private func referencedPrivateFunc(v: some V) -> some V { return v } + + /// Hidden underlying types. +// CHECK: Ignoring underlying information for opaque type of 'opaqueReferencingPrivate()' + func opaqueReferencingPrivate() -> some V { + referencedPrivateFunc(v: EV()) + } + +// CHECK: Ignoring underlying information for opaque type of 'opaqueReferencingPrivateVar' + var opaqueReferencingPrivateVar: some V { + referencedPrivateFunc(v: EV()) + } + +// CHECK: Ignoring underlying information for opaque type of 'opaqueReferencingPrivateVarPattern' + var opaqueReferencingPrivateVarPattern: some V { + get { + referencedPrivateFunc(v: EV()) + } + } + +// CHECK: Ignoring underlying information for opaque type of 'subscript(_:)' + subscript(v: some V) -> some V { + referencedPrivateFunc(v: v) + } + + /// Visible underlying types. +// CHECK: Loading underlying information for opaque type of 'inlinableOpaqueFunc()' + @inlinable + func inlinableOpaqueFunc() -> some V { EV() } + +// CHECK: Loading underlying information for opaque type of 'aeicOpaqueFunc()' + @_alwaysEmitIntoClient + func aeicOpaqueFunc() -> some V { EV() } + +// CHECK: Loading underlying information for opaque type of 'transparentOpaqueFunc()' + @_transparent + func transparentOpaqueFunc() -> some V { EV() } + +// CHECK: Loading underlying information for opaque type of 'inlinableOpaqueVar' + @inlinable + var inlinableOpaqueVar: some V { EV() } + +// CHECK: Loading underlying information for opaque type of 'inlinableOpaqueVarPattern' + var inlinableOpaqueVarPattern: some V { + @inlinable + get { EV() } + } +} + +//--- Client.swift +import Lib + +if #available(SwiftStdlib 5.1, *) { + let v = EV() + let _ = v.opaqueReferencingPrivate() + let _ = v.opaqueReferencingPrivateVar + let _ = v.opaqueReferencingPrivateVarPattern + let _ = v[v] + + let _ = v.inlinableOpaqueFunc() + let _ = v.aeicOpaqueFunc() + let _ = v.transparentOpaqueFunc() + + let _ = v.inlinableOpaqueVar + let _ = v.inlinableOpaqueVarPattern +}