diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index f2801c331e1f2..fce002173c8a6 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3214,6 +3214,7 @@ class OpaqueTypeDecl final : public GenericTypeDecl, private llvm::TrailingObjects { friend TrailingObjects; + friend class UniqueUnderlyingTypeSubstitutionsRequest; public: /// A set of substitutions that represents a possible underlying type iff @@ -3253,6 +3254,10 @@ class OpaqueTypeDecl final : mutable Identifier OpaqueReturnTypeIdentifier; + struct { + unsigned UniqueUnderlyingTypeComputed : 1; + } LazySemanticInfo = { }; + OpaqueTypeDecl(ValueDecl *NamingDecl, GenericParamList *GenericParams, DeclContext *DC, GenericSignature OpaqueInterfaceGenericSignature, @@ -3329,9 +3334,7 @@ class OpaqueTypeDecl final : /// 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 { - return UniqueUnderlyingType; - } + llvm::Optional getUniqueUnderlyingTypeSubstitutions() const; void setUniqueUnderlyingTypeSubstitutions(SubstitutionMap subs) { assert(!UniqueUnderlyingType.has_value() && "resetting underlying type?!"); diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 896dee72fad7d..a6daff4e7a233 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -4623,6 +4623,27 @@ class SemanticDeclAttrsRequest void cacheResult(DeclAttributes) const; }; +class UniqueUnderlyingTypeSubstitutionsRequest + : public SimpleRequest( + const OpaqueTypeDecl *), + RequestFlags::SeparatelyCached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + llvm::Optional evaluate(Evaluator &evaluator, + const OpaqueTypeDecl *) const; + +public: + // Separate caching. + bool isCached() const { return true; } + llvm::Optional> getCachedResult() const; + void cacheResult(llvm::Optional) const; +}; + #define SWIFT_TYPEID_ZONE TypeChecker #define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def" #include "swift/Basic/DefineTypeIDZone.h" diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 2fe1445ae5e42..b46788190ea79 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -527,3 +527,6 @@ SWIFT_REQUEST(TypeChecker, IsCCompatibleFuncDeclRequest, SWIFT_REQUEST(TypeChecker, SemanticDeclAttrsRequest, DeclAttributes(const Decl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, UniqueUnderlyingTypeSubstitutionsRequest, + llvm::Optional(const Decl *), + SeparatelyCached, NoLocationInfo) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 3c1ac729b781d..253da996fd3be 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -9608,6 +9608,12 @@ bool OpaqueTypeDecl::exportUnderlyingType() const { llvm_unreachable("The naming decl is expected to be either an AFD or ASD"); } +llvm::Optional +OpaqueTypeDecl::getUniqueUnderlyingTypeSubstitutions() const { + return evaluateOrDefault(getASTContext().evaluator, + UniqueUnderlyingTypeSubstitutionsRequest{this}, {}); +} + llvm::Optional OpaqueTypeDecl::getAnonymousOpaqueParamOrdinal(TypeRepr *repr) const { assert(NamingDeclAndHasOpaqueReturnTypeRepr.getInt() && diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 696130e58d1c3..2c2676def1534 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -2075,3 +2075,84 @@ void SemanticDeclAttrsRequest::cacheResult(DeclAttributes attrs) const { auto decl = std::get<0>(getStorage()); const_cast(decl)->setSemanticAttrsComputed(true); } + +//----------------------------------------------------------------------------// +// UniqueUnderlyingTypeSubstitutionsRequest computation. +//----------------------------------------------------------------------------// + +llvm::Optional +UniqueUnderlyingTypeSubstitutionsRequest::evaluate( + Evaluator &evaluator, const OpaqueTypeDecl *decl) const { + // Typechecking the body of a function that is associated with the naming + // declaration of an opaque type declaration will have the side-effect of + // setting UniqueUnderlyingType on the opaque type declaration. + auto typecheckBodyIfNeeded = [](AbstractFunctionDecl *afd) { + auto shouldTypecheckFunctionBody = [](AbstractFunctionDecl *afd) -> bool { + auto mod = afd->getModuleContext(); + if (!mod->isMainModule()) + return true; + + // If the main module has no primary source files then the compilation is + // a whole module build and all source files can be typechecked. + if (mod->getPrimarySourceFiles().size() == 0) + return true; + + auto sf = afd->getParentSourceFile(); + if (!sf) + return true; + + if (sf->isPrimary()) + return true; + + switch (sf->Kind) { + case SourceFileKind::Interface: + case SourceFileKind::MacroExpansion: + case SourceFileKind::SIL: + return true; + case SourceFileKind::Main: + case SourceFileKind::Library: + // Don't typecheck bodies in auxiliary source files. + return false; + } + + llvm_unreachable("bad SourceFileKind"); + }; + + if (shouldTypecheckFunctionBody(afd)) + (void)afd->getTypecheckedBody(); + }; + + auto namingDecl = decl->getNamingDecl(); + if (auto afd = dyn_cast(namingDecl)) { + typecheckBodyIfNeeded(afd); + + return decl->UniqueUnderlyingType; + } + + if (auto asd = dyn_cast(namingDecl)) { + asd->visitParsedAccessors([&](AccessorDecl *accessor) { + typecheckBodyIfNeeded(accessor); + }); + + return decl->UniqueUnderlyingType; + } + + assert(false && "Unexpected kind of naming decl"); + return llvm::None; +} + +llvm::Optional> +UniqueUnderlyingTypeSubstitutionsRequest::getCachedResult() const { + auto decl = std::get<0>(getStorage()); + if (decl->LazySemanticInfo.UniqueUnderlyingTypeComputed) + return decl->UniqueUnderlyingType; + return llvm::None; +} + +void UniqueUnderlyingTypeSubstitutionsRequest::cacheResult( + llvm::Optional subs) const { + auto decl = std::get<0>(getStorage()); + assert(subs == decl->UniqueUnderlyingType); + const_cast(decl) + ->LazySemanticInfo.UniqueUnderlyingTypeComputed = true; +} diff --git a/test/SILGen/lazy_typecheck_opaque_result_type.swift b/test/SILGen/lazy_typecheck_opaque_result_type.swift new file mode 100644 index 0000000000000..54a4fda559b76 --- /dev/null +++ b/test/SILGen/lazy_typecheck_opaque_result_type.swift @@ -0,0 +1,139 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t +// RUN: %target-swift-frontend -emit-module %t/Library.swift -parse-as-library -module-name Library -enable-library-evolution -emit-module-path %t/Library.swiftmodule +// RUN: %target-swift-frontend -emit-silgen -primary-file %t/Primary.swift %t/Other.swift -parse-as-library -module-name Test -I %t | %FileCheck %s --check-prefixes CHECK,CHECK-PRIMARY,CHECK-COMMON +// RUN: %target-swift-frontend -emit-silgen %t/Primary.swift %t/Other.swift -parse-as-library -module-name Test -I %t | %FileCheck %s --check-prefixes CHECK,CHECK-WHOLE-MODULE,CHECK-COMMON +// RUN: %target-swift-frontend -emit-silgen -primary-file %t/Primary.swift %t/Other.swift -parse-as-library -module-name Test -I %t -experimental-lazy-typecheck | %FileCheck %s --check-prefixes CHECK,CHECK-PRIMARY,CHECK-COMMON +// RUN: %target-swift-frontend -emit-silgen -primary-file %t/Primary.swift %t/Other.swift -parse-as-library -module-name Test -I %t -experimental-skip-non-inlinable-function-bodies | %FileCheck %s --check-prefixes CHECK-SKIP,CHECK-COMMON + +//--- Library.swift + +public protocol P {} + +@usableFromInline struct LibraryStruct: P { + @usableFromInline init() {} +} + +@available(SwiftStdlib 5.5, *) +public func returnsLibraryStruct() -> some P { + return LibraryStruct() +} + +@available(SwiftStdlib 5.5, *) +@inlinable public func inlinableReturnsLibraryStruct() -> some P { + return LibraryStruct() +} + +//--- Other.swift + +import Library + +struct OtherStruct: P {} + +@available(SwiftStdlib 5.5, *) +public func returnsOtherStruct() -> some P { + return OtherStruct() +} + +//--- Primary.swift + +import Library + +public struct PrimaryStruct: P { + public init() {} +} + +// CHECK-LABEL: sil{{.*}} @$s4Test20returnsPrimaryStructQryF : $@convention(thin) @substituted <τ_0_0> () -> @out τ_0_0 for <@_opaqueReturnTypeOf("$s4Test20returnsPrimaryStructQryF", 0) __> { +// CHECK: bb0(%0 : $*PrimaryStruct): +// CHECK: } // end sil function '$s4Test20returnsPrimaryStructQryF' +@available(SwiftStdlib 5.5, *) +public func returnsPrimaryStruct() -> some P { + return PrimaryStruct() +} + +// CHECK-LABEL: sil{{.*}} @$s4Test39globalComputedVarReturningPrimaryStructQrvg : $@convention(thin) @substituted <τ_0_0> () -> @out τ_0_0 for <@_opaqueReturnTypeOf("$s4Test39globalComputedVarReturningPrimaryStructQrvp", 0) __> { +// CHECK: bb0(%0 : $*PrimaryStruct): +// CHECK: } // end sil function '$s4Test39globalComputedVarReturningPrimaryStructQrvg' +@available(SwiftStdlib 5.5, *) +public var globalComputedVarReturningPrimaryStruct: some P { + return PrimaryStruct() +} + +@available(SwiftStdlib 5.5, *) +public struct S { + private var privatePrimaryStruct: PrimaryStruct + + public var computedVarReturningPrimaryStruct: some P { + // CHECK-LABEL: sil{{.*}} @$s4Test1SV33computedVarReturningPrimaryStructQrvg : $@convention(method) (S) -> @out @_opaqueReturnTypeOf("$s4Test1SV33computedVarReturningPrimaryStructQrvp", 0) __ { + // CHECK: bb0(%0 : $*PrimaryStruct, %1 : $S): + // CHECK: } // end sil function '$s4Test1SV33computedVarReturningPrimaryStructQrvg' + get { privatePrimaryStruct } + + // CHECK-LABEL: sil{{.*}} @$s4Test1SV33computedVarReturningPrimaryStructQrvs : $@convention(method) (@in @_opaqueReturnTypeOf("$s4Test1SV33computedVarReturningPrimaryStructQrvp", 0) __, @inout S) -> () { + // CHECK: bb0(%0 : $*PrimaryStruct, %1 : $*S): + // CHECK: } // end sil function '$s4Test1SV33computedVarReturningPrimaryStructQrvs' + set {} + } + + public subscript(subscriptReturningPrimaryStruct: Void) -> some P { + // CHECK-LABEL: sil{{.*}} @$s4Test1SVyQryt_tcig : $@convention(method) (S) -> @out @_opaqueReturnTypeOf("$s4Test1SVyQryt_tcip", 0) __ { + // CHECK: bb0(%0 : $*PrimaryStruct, %1 : $S): + // CHECK: } // end sil function '$s4Test1SVyQryt_tcig' + get { privatePrimaryStruct } + // CHECK-LABEL: sil{{.*}} @$s4Test1SVyQryt_tcis : $@convention(method) (@in @_opaqueReturnTypeOf("$s4Test1SVyQryt_tcip", 0) __, @inout S) -> () { + // CHECK: bb0(%0 : $*PrimaryStruct, %1 : $*S): + // CHECK: } // end sil function '$s4Test1SVyQryt_tcis' + set {} + } +} + +// CHECK-COMMON-LABEL: sil{{.*}} @$s4Test024inlinableReturnsResultOfC13PrimaryStructQryF : $@convention(thin) @substituted <τ_0_0> () -> @out τ_0_0 for <@_opaqueReturnTypeOf("$s4Test024inlinableReturnsResultOfC13PrimaryStructQryF", 0) __> { +// CHECK: bb0(%0 : $*PrimaryStruct): +// CHECK-SKIP: bb0(%0 : $*@_opaqueReturnTypeOf("$s4Test20returnsPrimaryStructQryF", 0) __): +// CHECK-COMMON: } // end sil function '$s4Test024inlinableReturnsResultOfC13PrimaryStructQryF' +@available(SwiftStdlib 5.5, *) +@inlinable public func inlinableReturnsResultOfReturnsPrimaryStruct() -> some P { + return returnsPrimaryStruct() +} + +// CHECK-LABEL: sil{{.*}} @$s4Test19returnsNestedStructQryF : $@convention(thin) @substituted <τ_0_0> () -> @out τ_0_0 for <@_opaqueReturnTypeOf("$s4Test19returnsNestedStructQryF", 0) __> { +// CHECK: bb0(%0 : $*NestedStruct): +// CHECK: } // end sil function '$s4Test19returnsNestedStructQryF' +@available(SwiftStdlib 5.5, *) +public func returnsNestedStruct() -> some P { + struct NestedStruct: P {} + return NestedStruct() +} + +// CHECK-LABEL: sil{{.*}} @$s4Test34returnsResultOfReturnsNestedStructQryF : $@convention(thin) @substituted <τ_0_0> () -> @out τ_0_0 for <@_opaqueReturnTypeOf("$s4Test34returnsResultOfReturnsNestedStructQryF", 0) __> { +// CHECK: bb0(%0 : $*NestedStruct): +// CHECK: } // end sil function '$s4Test34returnsResultOfReturnsNestedStructQryF' +@available(SwiftStdlib 5.5, *) +public func returnsResultOfReturnsNestedStruct() -> some P { + return returnsNestedStruct() +} + +// CHECK-LABEL: sil{{.*}} @$s4Test33returnsResultOfReturnsOtherStructQryF : $@convention(thin) @substituted <τ_0_0> () -> @out τ_0_0 for <@_opaqueReturnTypeOf("$s4Test33returnsResultOfReturnsOtherStructQryF", 0) __> { +// CHECK-PRIMARY: bb0(%0 : $*@_opaqueReturnTypeOf("$s4Test18returnsOtherStructQryF", 0) __): +// CHECK-WHOLE-MODULE: bb0(%0 : $*OtherStruct): +// CHECK: } // end sil function '$s4Test33returnsResultOfReturnsOtherStructQryF' +@available(SwiftStdlib 5.5, *) +public func returnsResultOfReturnsOtherStruct() -> some P { + return returnsOtherStruct() +} + +// CHECK-LABEL: sil{{.*}} @$s4Test35returnsResultOfReturnsLibraryStructQryF : $@convention(thin) @substituted <τ_0_0> () -> @out τ_0_0 for <@_opaqueReturnTypeOf("$s4Test35returnsResultOfReturnsLibraryStructQryF", 0) __> { +// CHECK: bb0(%0 : $*@_opaqueReturnTypeOf("$s7Library07returnsA6StructQryF", 0) __): +// CHECK: } // end sil function '$s4Test35returnsResultOfReturnsLibraryStructQryF' +@available(SwiftStdlib 5.5, *) +public func returnsResultOfReturnsLibraryStruct() -> some P { + return returnsLibraryStruct() +} + +// CHECK-LABEL: sil{{.*}} @$s4Test44returnsResulfOfInlinableReturnsLibraryStructQryF : $@convention(thin) @substituted <τ_0_0> () -> @out τ_0_0 for <@_opaqueReturnTypeOf("$s4Test44returnsResulfOfInlinableReturnsLibraryStructQryF", 0) __> { +// CHECK: bb0(%0 : $*LibraryStruct): +// CHECK: } // end sil function '$s4Test44returnsResulfOfInlinableReturnsLibraryStructQryF' +@available(SwiftStdlib 5.5, *) +public func returnsResulfOfInlinableReturnsLibraryStruct() -> some P { + return inlinableReturnsLibraryStruct() +} diff --git a/test/SILOptimizer/specialize_opaque_result_types2.sil b/test/SILOptimizer/specialize_opaque_result_types2.sil index 07687dce9f127..926fc42958c65 100644 --- a/test/SILOptimizer/specialize_opaque_result_types2.sil +++ b/test/SILOptimizer/specialize_opaque_result_types2.sil @@ -2,7 +2,7 @@ // RUN: %target-swift-frontend -disable-availability-checking -primary-file %S/Inputs/specialize_opaque_result_types.swift -enable-library-evolution -module-name A -emit-sib -o %t/A.sib // RUN: %target-swift-frontend -emit-sil -primary-file %s -enable-library-evolution -O -module-name A %t/A.sib -o - | %FileCheck %s -// REQUIRES: CPU=x86_64 +// REQUIRES: CPU=x86_64 || CPU=arm64 sil_stage canonical