Skip to content

Commit 2709743

Browse files
committed
Serialization: Lazily deserialize OpaqueTypeDecl's underlying substitutions
We need to serialize the underlying type substitution map for an inlinable function. However, there is no reason to deserialize it eagerly, since doing so can lead to cycles. It is better for correctness and performance to only deserialize it when needed. Technically this fixes a regression from #84299, but the actual problem was there all along, it was just exposed by my change on a specific project. Fixes rdar://163301203.
1 parent cf535d8 commit 2709743

File tree

13 files changed

+362
-183
lines changed

13 files changed

+362
-183
lines changed

include/swift/AST/Decl.h

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,11 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
761761
HasAnyUnavailableDuringLoweringValues : 1
762762
);
763763

764+
SWIFT_INLINE_BITFIELD(OpaqueTypeDecl, GenericTypeDecl, 1,
765+
/// Whether we have lazily-loaded underlying substitutions.
766+
HasLazyUnderlyingSubstitutions : 1
767+
);
768+
764769
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+8,
765770
/// If the module is compiled as static library.
766771
StaticLibrary : 1,
@@ -3646,7 +3651,8 @@ class OpaqueTypeDecl final :
36463651
OpaqueTypeDecl(ValueDecl *NamingDecl, GenericParamList *GenericParams,
36473652
DeclContext *DC,
36483653
GenericSignature OpaqueInterfaceGenericSignature,
3649-
ArrayRef<TypeRepr *> OpaqueReturnTypeReprs);
3654+
ArrayRef<TypeRepr *> OpaqueReturnTypeReprs,
3655+
bool hasLazyUnderlyingSubstitutions);
36503656

36513657
unsigned getNumOpaqueReturnTypeReprs() const {
36523658
return NamingDeclAndHasOpaqueReturnTypeRepr.getInt()
@@ -3658,13 +3664,21 @@ class OpaqueTypeDecl final :
36583664
return getNumOpaqueReturnTypeReprs();
36593665
}
36603666

3667+
void loadLazyUnderlyingSubstitutions();
3668+
36613669
public:
3662-
static OpaqueTypeDecl *get(
3670+
static OpaqueTypeDecl *create(
36633671
ValueDecl *NamingDecl, GenericParamList *GenericParams,
36643672
DeclContext *DC,
36653673
GenericSignature OpaqueInterfaceGenericSignature,
36663674
ArrayRef<TypeRepr *> OpaqueReturnTypeReprs);
36673675

3676+
static OpaqueTypeDecl *createDeserialized(
3677+
GenericParamList *GenericParams,
3678+
DeclContext *DC,
3679+
GenericSignature OpaqueInterfaceGenericSignature,
3680+
LazyMemberLoader *lazyLoader, uint64_t underlyingSubsData);
3681+
36683682
ValueDecl *getNamingDecl() const {
36693683
return NamingDeclAndHasOpaqueReturnTypeRepr.getPointer();
36703684
}
@@ -3720,19 +3734,19 @@ class OpaqueTypeDecl final :
37203734
bool typeCheckFunctionBodies=true) const;
37213735

37223736
void setUniqueUnderlyingTypeSubstitutions(SubstitutionMap subs) {
3723-
assert(!UniqueUnderlyingType.has_value() && "resetting underlying type?!");
3737+
ASSERT(!Bits.OpaqueTypeDecl.HasLazyUnderlyingSubstitutions);
3738+
ASSERT(!UniqueUnderlyingType.has_value() && "resetting underlying type?!");
37243739
UniqueUnderlyingType = subs;
37253740
}
37263741

37273742
bool hasConditionallyAvailableSubstitutions() const {
3743+
const_cast<OpaqueTypeDecl *>(this)->loadLazyUnderlyingSubstitutions();
3744+
37283745
return ConditionallyAvailableTypes.has_value();
37293746
}
37303747

37313748
ArrayRef<ConditionallyAvailableSubstitutions *>
3732-
getConditionallyAvailableSubstitutions() const {
3733-
assert(ConditionallyAvailableTypes);
3734-
return ConditionallyAvailableTypes.value();
3735-
}
3749+
getConditionallyAvailableSubstitutions() const;
37363750

37373751
void setConditionallyAvailableSubstitutions(
37383752
ArrayRef<ConditionallyAvailableSubstitutions *> substitutions);

include/swift/AST/LazyResolver.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ class LazyAssociatedTypeData : public LazyContextData {
6363
uint64_t defaultDefinitionTypeData = 0;
6464
};
6565

66+
class LazyOpaqueTypeData : public LazyContextData {
67+
public:
68+
/// The context data used for loading the underlying type substitution map.
69+
uint64_t underlyingSubsData = 0;
70+
};
71+
6672
/// Context data for protocols.
6773
class LazyProtocolData : public LazyIterableDeclContextData {
6874
public:
@@ -138,6 +144,10 @@ class alignas(void*) LazyMemberLoader {
138144
// Returns the target parameter of the `@_specialize` attribute or null.
139145
virtual ValueDecl *loadTargetFunctionDecl(const AbstractSpecializeAttr *attr,
140146
uint64_t contextData) = 0;
147+
148+
/// Loads the underlying type substitution map of an opaque result declaration.
149+
virtual void
150+
finishOpaqueTypeDecl(OpaqueTypeDecl *opaqueDecl, uint64_t contextData) = 0;
141151
};
142152

143153
/// A class that can lazily load conformances from a serialized format.

lib/AST/ASTContext.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3174,20 +3174,22 @@ ASTContext::getOrCreateLazyContextData(const Decl *decl,
31743174
LazyMemberLoader *lazyLoader) {
31753175
if (auto *data = getLazyContextData(decl)) {
31763176
// Make sure we didn't provide an incompatible lazy loader.
3177-
assert(!lazyLoader || lazyLoader == data->loader);
3177+
ASSERT(!lazyLoader || lazyLoader == data->loader);
31783178
return data;
31793179
}
31803180

31813181
LazyContextData *&entry = getImpl().LazyContexts[decl];
31823182

31833183
// Create new lazy context data with the given loader.
3184-
assert(lazyLoader && "Queried lazy data for non-lazy iterable context");
3184+
ASSERT(lazyLoader && "Queried lazy data for non-lazy iterable context");
31853185
if (isa<ProtocolDecl>(decl)) {
31863186
entry = Allocate<LazyProtocolData>();
31873187
} else if (isa<NominalTypeDecl>(decl) || isa<ExtensionDecl>(decl)) {
31883188
entry = Allocate<LazyIterableDeclContextData>();
3189+
} else if (isa<OpaqueTypeDecl>(decl)) {
3190+
entry = Allocate<LazyOpaqueTypeData>();
31893191
} else {
3190-
assert(isa<AssociatedTypeDecl>(decl));
3192+
ASSERT(isa<AssociatedTypeDecl>(decl));
31913193
entry = Allocate<LazyAssociatedTypeData>();
31923194
}
31933195

lib/AST/Decl.cpp

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10820,13 +10820,16 @@ bool AbstractFunctionDecl::isResilient(ModuleDecl *M,
1082010820
OpaqueTypeDecl::OpaqueTypeDecl(ValueDecl *NamingDecl,
1082110821
GenericParamList *GenericParams, DeclContext *DC,
1082210822
GenericSignature OpaqueInterfaceGenericSignature,
10823-
ArrayRef<TypeRepr *>
10824-
OpaqueReturnTypeReprs)
10823+
ArrayRef<TypeRepr *> OpaqueReturnTypeReprs,
10824+
bool hasLazyUnderlyingSubstitutions)
1082510825
: GenericTypeDecl(DeclKind::OpaqueType, DC, Identifier(), SourceLoc(), {},
1082610826
GenericParams),
1082710827
NamingDeclAndHasOpaqueReturnTypeRepr(
1082810828
NamingDecl, !OpaqueReturnTypeReprs.empty()),
1082910829
OpaqueInterfaceGenericSignature(OpaqueInterfaceGenericSignature) {
10830+
Bits.OpaqueTypeDecl.HasLazyUnderlyingSubstitutions
10831+
= hasLazyUnderlyingSubstitutions;
10832+
1083010833
// Always implicit.
1083110834
setImplicit();
1083210835

@@ -10839,7 +10842,7 @@ OpaqueTypeDecl::OpaqueTypeDecl(ValueDecl *NamingDecl,
1083910842
OpaqueReturnTypeReprs.end(), getTrailingObjects());
1084010843
}
1084110844

10842-
OpaqueTypeDecl *OpaqueTypeDecl::get(
10845+
OpaqueTypeDecl *OpaqueTypeDecl::create(
1084310846
ValueDecl *NamingDecl, GenericParamList *GenericParams,
1084410847
DeclContext *DC,
1084510848
GenericSignature OpaqueInterfaceGenericSignature,
@@ -10850,7 +10853,33 @@ OpaqueTypeDecl *OpaqueTypeDecl::get(
1085010853
auto mem = ctx.Allocate(size, alignof(OpaqueTypeDecl));
1085110854
return new (mem) OpaqueTypeDecl(
1085210855
NamingDecl, GenericParams, DC, OpaqueInterfaceGenericSignature,
10853-
OpaqueReturnTypeReprs);
10856+
OpaqueReturnTypeReprs, /*hasLazyUnderlyingSubstitutions=*/false);
10857+
}
10858+
10859+
OpaqueTypeDecl *OpaqueTypeDecl::createDeserialized(
10860+
GenericParamList *GenericParams, DeclContext *DC,
10861+
GenericSignature OpaqueInterfaceGenericSignature,
10862+
LazyMemberLoader *lazyLoader, uint64_t underlyingSubsData) {
10863+
bool hasLazyUnderlyingSubstitutions = (underlyingSubsData != 0);
10864+
10865+
ASTContext &ctx = DC->getASTContext();
10866+
auto size = totalSizeToAlloc<TypeRepr *>(0);
10867+
auto mem = ctx.Allocate(size, alignof(OpaqueTypeDecl));
10868+
10869+
// NamingDecl is set later by deserialization
10870+
auto *decl = new (mem) OpaqueTypeDecl(
10871+
/*namingDecl=*/nullptr, GenericParams, DC,
10872+
OpaqueInterfaceGenericSignature, { },
10873+
hasLazyUnderlyingSubstitutions);
10874+
10875+
if (hasLazyUnderlyingSubstitutions) {
10876+
auto &ctx = DC->getASTContext();
10877+
auto *data = static_cast<LazyOpaqueTypeData *>(
10878+
ctx.getOrCreateLazyContextData(decl, lazyLoader));
10879+
data->underlyingSubsData = underlyingSubsData;
10880+
}
10881+
10882+
return decl;
1085410883
}
1085510884

1085610885
bool OpaqueTypeDecl::isOpaqueReturnTypeOf(const Decl *ownerDecl) const {
@@ -10906,16 +10935,41 @@ bool OpaqueTypeDecl::exportUnderlyingType() const {
1090610935
llvm_unreachable("The naming decl is expected to be either an AFD or ASD");
1090710936
}
1090810937

10938+
void OpaqueTypeDecl::loadLazyUnderlyingSubstitutions() {
10939+
if (!Bits.OpaqueTypeDecl.HasLazyUnderlyingSubstitutions)
10940+
return;
10941+
10942+
Bits.OpaqueTypeDecl.HasLazyUnderlyingSubstitutions = 0;
10943+
10944+
auto &ctx = getASTContext();
10945+
auto *data = static_cast<LazyOpaqueTypeData *>(
10946+
ctx.getLazyContextData(this));
10947+
ASSERT(data != nullptr);
10948+
10949+
data->loader->finishOpaqueTypeDecl(
10950+
this, data->underlyingSubsData);
10951+
}
10952+
1090910953
std::optional<SubstitutionMap>
1091010954
OpaqueTypeDecl::getUniqueUnderlyingTypeSubstitutions(
1091110955
bool typeCheckFunctionBodies) const {
10956+
const_cast<OpaqueTypeDecl *>(this)->loadLazyUnderlyingSubstitutions();
10957+
1091210958
if (!typeCheckFunctionBodies)
1091310959
return UniqueUnderlyingType;
1091410960

1091510961
return evaluateOrDefault(getASTContext().evaluator,
1091610962
UniqueUnderlyingTypeSubstitutionsRequest{this}, {});
1091710963
}
1091810964

10965+
ArrayRef<OpaqueTypeDecl::ConditionallyAvailableSubstitutions *>
10966+
OpaqueTypeDecl::getConditionallyAvailableSubstitutions() const {
10967+
const_cast<OpaqueTypeDecl *>(this)->loadLazyUnderlyingSubstitutions();
10968+
10969+
assert(ConditionallyAvailableTypes);
10970+
return ConditionallyAvailableTypes.value();
10971+
}
10972+
1091910973
std::optional<unsigned>
1092010974
OpaqueTypeDecl::getAnonymousOpaqueParamOrdinal(TypeRepr *repr) const {
1092110975
assert(NamingDeclAndHasOpaqueReturnTypeRepr.getInt() &&
@@ -10945,7 +10999,8 @@ Identifier OpaqueTypeDecl::getOpaqueReturnTypeIdentifier() const {
1094510999

1094611000
void OpaqueTypeDecl::setConditionallyAvailableSubstitutions(
1094711001
ArrayRef<ConditionallyAvailableSubstitutions *> substitutions) {
10948-
assert(!ConditionallyAvailableTypes &&
11002+
ASSERT(!Bits.OpaqueTypeDecl.HasLazyUnderlyingSubstitutions);
11003+
ASSERT(!ConditionallyAvailableTypes &&
1094911004
"resetting conditionally available substitutions?!");
1095011005
ConditionallyAvailableTypes = getASTContext().AllocateCopy(substitutions);
1095111006
}

lib/ClangImporter/ImporterImpl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1762,6 +1762,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
17621762
llvm_unreachable("unimplemented for ClangImporter");
17631763
}
17641764

1765+
void finishOpaqueTypeDecl(OpaqueTypeDecl *decl,
1766+
uint64_t contextData) override {
1767+
llvm_unreachable("unimplemented for ClangImporter");
1768+
}
1769+
17651770
template <typename DeclTy, typename ...Targs>
17661771
DeclTy *createDeclWithClangNode(ClangNode ClangN, AccessLevel access,
17671772
Targs &&... Args) {

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ OpaqueResultTypeRequest::evaluate(Evaluator &evaluator,
215215
}
216216

217217
// Create the OpaqueTypeDecl for the result type.
218-
auto opaqueDecl = OpaqueTypeDecl::get(
218+
auto opaqueDecl = OpaqueTypeDecl::create(
219219
originatingDecl, genericParams, parentDC, interfaceSignature,
220220
opaqueReprs);
221221
if (auto originatingSig = originatingDC->getGenericSignatureOfContext()) {

lib/Serialization/DeclTypeRecordNodes.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,11 @@ OTHER(ERROR_FLAG, 155)
215215
OTHER(ABI_ONLY_COUNTERPART, 156)
216216
OTHER(DECL_NAME_REF, 157)
217217

218+
TRAILING_INFO(UNDERLYING_SUBSTITUTION)
218219
TRAILING_INFO(CONDITIONAL_SUBSTITUTION)
219220
TRAILING_INFO(CONDITIONAL_SUBSTITUTION_COND)
220221

221-
OTHER(LIFETIME_DEPENDENCE, 160)
222+
OTHER(LIFETIME_DEPENDENCE, 161)
222223
TRAILING_INFO(INHERITED_PROTOCOLS)
223224

224225
#ifndef DECL_ATTR

0 commit comments

Comments
 (0)