Skip to content

AST: Requestify unique underlying type substitutions #69770

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3214,6 +3214,7 @@ class OpaqueTypeDecl final :
public GenericTypeDecl,
private llvm::TrailingObjects<OpaqueTypeDecl, TypeRepr *> {
friend TrailingObjects;
friend class UniqueUnderlyingTypeSubstitutionsRequest;

public:
/// A set of substitutions that represents a possible underlying type iff
Expand Down Expand Up @@ -3253,6 +3254,10 @@ class OpaqueTypeDecl final :

mutable Identifier OpaqueReturnTypeIdentifier;

struct {
unsigned UniqueUnderlyingTypeComputed : 1;
} LazySemanticInfo = { };

OpaqueTypeDecl(ValueDecl *NamingDecl, GenericParamList *GenericParams,
DeclContext *DC,
GenericSignature OpaqueInterfaceGenericSignature,
Expand Down Expand Up @@ -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<SubstitutionMap> getUniqueUnderlyingTypeSubstitutions() const {
return UniqueUnderlyingType;
}
llvm::Optional<SubstitutionMap> getUniqueUnderlyingTypeSubstitutions() const;

void setUniqueUnderlyingTypeSubstitutions(SubstitutionMap subs) {
assert(!UniqueUnderlyingType.has_value() && "resetting underlying type?!");
Expand Down
21 changes: 21 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -4623,6 +4623,27 @@ class SemanticDeclAttrsRequest
void cacheResult(DeclAttributes) const;
};

class UniqueUnderlyingTypeSubstitutionsRequest
: public SimpleRequest<UniqueUnderlyingTypeSubstitutionsRequest,
llvm::Optional<SubstitutionMap>(
const OpaqueTypeDecl *),
RequestFlags::SeparatelyCached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

llvm::Optional<SubstitutionMap> evaluate(Evaluator &evaluator,
const OpaqueTypeDecl *) const;

public:
// Separate caching.
bool isCached() const { return true; }
llvm::Optional<llvm::Optional<SubstitutionMap>> getCachedResult() const;
void cacheResult(llvm::Optional<SubstitutionMap>) const;
};

#define SWIFT_TYPEID_ZONE TypeChecker
#define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def"
#include "swift/Basic/DefineTypeIDZone.h"
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -527,3 +527,6 @@ SWIFT_REQUEST(TypeChecker, IsCCompatibleFuncDeclRequest,
SWIFT_REQUEST(TypeChecker, SemanticDeclAttrsRequest,
DeclAttributes(const Decl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, UniqueUnderlyingTypeSubstitutionsRequest,
llvm::Optional<SubstitutionMap>(const Decl *),
SeparatelyCached, NoLocationInfo)
6 changes: 6 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9608,6 +9608,12 @@ bool OpaqueTypeDecl::exportUnderlyingType() const {
llvm_unreachable("The naming decl is expected to be either an AFD or ASD");
}

llvm::Optional<SubstitutionMap>
OpaqueTypeDecl::getUniqueUnderlyingTypeSubstitutions() const {
return evaluateOrDefault(getASTContext().evaluator,
UniqueUnderlyingTypeSubstitutionsRequest{this}, {});
}

llvm::Optional<unsigned>
OpaqueTypeDecl::getAnonymousOpaqueParamOrdinal(TypeRepr *repr) const {
assert(NamingDeclAndHasOpaqueReturnTypeRepr.getInt() &&
Expand Down
81 changes: 81 additions & 0 deletions lib/AST/TypeCheckRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2075,3 +2075,84 @@ void SemanticDeclAttrsRequest::cacheResult(DeclAttributes attrs) const {
auto decl = std::get<0>(getStorage());
const_cast<Decl *>(decl)->setSemanticAttrsComputed(true);
}

//----------------------------------------------------------------------------//
// UniqueUnderlyingTypeSubstitutionsRequest computation.
//----------------------------------------------------------------------------//

llvm::Optional<SubstitutionMap>
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<AbstractFunctionDecl>(namingDecl)) {
typecheckBodyIfNeeded(afd);

return decl->UniqueUnderlyingType;
}

if (auto asd = dyn_cast<AbstractStorageDecl>(namingDecl)) {
asd->visitParsedAccessors([&](AccessorDecl *accessor) {
typecheckBodyIfNeeded(accessor);
});

return decl->UniqueUnderlyingType;
}

assert(false && "Unexpected kind of naming decl");
return llvm::None;
}

llvm::Optional<llvm::Optional<SubstitutionMap>>
UniqueUnderlyingTypeSubstitutionsRequest::getCachedResult() const {
auto decl = std::get<0>(getStorage());
if (decl->LazySemanticInfo.UniqueUnderlyingTypeComputed)
return decl->UniqueUnderlyingType;
return llvm::None;
}

void UniqueUnderlyingTypeSubstitutionsRequest::cacheResult(
llvm::Optional<SubstitutionMap> subs) const {
auto decl = std::get<0>(getStorage());
assert(subs == decl->UniqueUnderlyingType);
const_cast<OpaqueTypeDecl *>(decl)
->LazySemanticInfo.UniqueUnderlyingTypeComputed = true;
}
139 changes: 139 additions & 0 deletions test/SILGen/lazy_typecheck_opaque_result_type.swift
Original file line number Diff line number Diff line change
@@ -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()
}
2 changes: 1 addition & 1 deletion test/SILOptimizer/specialize_opaque_result_types2.sil
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down