Skip to content

Commit e5326e8

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 d4ae238 commit e5326e8

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
@@ -10810,13 +10810,16 @@ bool AbstractFunctionDecl::isResilient(ModuleDecl *M,
1081010810
OpaqueTypeDecl::OpaqueTypeDecl(ValueDecl *NamingDecl,
1081110811
GenericParamList *GenericParams, DeclContext *DC,
1081210812
GenericSignature OpaqueInterfaceGenericSignature,
10813-
ArrayRef<TypeRepr *>
10814-
OpaqueReturnTypeReprs)
10813+
ArrayRef<TypeRepr *> OpaqueReturnTypeReprs,
10814+
bool hasLazyUnderlyingSubstitutions)
1081510815
: GenericTypeDecl(DeclKind::OpaqueType, DC, Identifier(), SourceLoc(), {},
1081610816
GenericParams),
1081710817
NamingDeclAndHasOpaqueReturnTypeRepr(
1081810818
NamingDecl, !OpaqueReturnTypeReprs.empty()),
1081910819
OpaqueInterfaceGenericSignature(OpaqueInterfaceGenericSignature) {
10820+
Bits.OpaqueTypeDecl.HasLazyUnderlyingSubstitutions
10821+
= hasLazyUnderlyingSubstitutions;
10822+
1082010823
// Always implicit.
1082110824
setImplicit();
1082210825

@@ -10829,7 +10832,7 @@ OpaqueTypeDecl::OpaqueTypeDecl(ValueDecl *NamingDecl,
1082910832
OpaqueReturnTypeReprs.end(), getTrailingObjects());
1083010833
}
1083110834

10832-
OpaqueTypeDecl *OpaqueTypeDecl::get(
10835+
OpaqueTypeDecl *OpaqueTypeDecl::create(
1083310836
ValueDecl *NamingDecl, GenericParamList *GenericParams,
1083410837
DeclContext *DC,
1083510838
GenericSignature OpaqueInterfaceGenericSignature,
@@ -10840,7 +10843,33 @@ OpaqueTypeDecl *OpaqueTypeDecl::get(
1084010843
auto mem = ctx.Allocate(size, alignof(OpaqueTypeDecl));
1084110844
return new (mem) OpaqueTypeDecl(
1084210845
NamingDecl, GenericParams, DC, OpaqueInterfaceGenericSignature,
10843-
OpaqueReturnTypeReprs);
10846+
OpaqueReturnTypeReprs, /*hasLazyUnderlyingSubstitutions=*/false);
10847+
}
10848+
10849+
OpaqueTypeDecl *OpaqueTypeDecl::createDeserialized(
10850+
GenericParamList *GenericParams, DeclContext *DC,
10851+
GenericSignature OpaqueInterfaceGenericSignature,
10852+
LazyMemberLoader *lazyLoader, uint64_t underlyingSubsData) {
10853+
bool hasLazyUnderlyingSubstitutions = (underlyingSubsData != 0);
10854+
10855+
ASTContext &ctx = DC->getASTContext();
10856+
auto size = totalSizeToAlloc<TypeRepr *>(0);
10857+
auto mem = ctx.Allocate(size, alignof(OpaqueTypeDecl));
10858+
10859+
// NamingDecl is set later by deserialization
10860+
auto *decl = new (mem) OpaqueTypeDecl(
10861+
/*namingDecl=*/nullptr, GenericParams, DC,
10862+
OpaqueInterfaceGenericSignature, { },
10863+
hasLazyUnderlyingSubstitutions);
10864+
10865+
if (hasLazyUnderlyingSubstitutions) {
10866+
auto &ctx = DC->getASTContext();
10867+
auto *data = static_cast<LazyOpaqueTypeData *>(
10868+
ctx.getOrCreateLazyContextData(decl, lazyLoader));
10869+
data->underlyingSubsData = underlyingSubsData;
10870+
}
10871+
10872+
return decl;
1084410873
}
1084510874

1084610875
bool OpaqueTypeDecl::isOpaqueReturnTypeOf(const Decl *ownerDecl) const {
@@ -10896,16 +10925,41 @@ bool OpaqueTypeDecl::exportUnderlyingType() const {
1089610925
llvm_unreachable("The naming decl is expected to be either an AFD or ASD");
1089710926
}
1089810927

10928+
void OpaqueTypeDecl::loadLazyUnderlyingSubstitutions() {
10929+
if (!Bits.OpaqueTypeDecl.HasLazyUnderlyingSubstitutions)
10930+
return;
10931+
10932+
Bits.OpaqueTypeDecl.HasLazyUnderlyingSubstitutions = 0;
10933+
10934+
auto &ctx = getASTContext();
10935+
auto *data = static_cast<LazyOpaqueTypeData *>(
10936+
ctx.getLazyContextData(this));
10937+
ASSERT(data != nullptr);
10938+
10939+
data->loader->finishOpaqueTypeDecl(
10940+
this, data->underlyingSubsData);
10941+
}
10942+
1089910943
std::optional<SubstitutionMap>
1090010944
OpaqueTypeDecl::getUniqueUnderlyingTypeSubstitutions(
1090110945
bool typeCheckFunctionBodies) const {
10946+
const_cast<OpaqueTypeDecl *>(this)->loadLazyUnderlyingSubstitutions();
10947+
1090210948
if (!typeCheckFunctionBodies)
1090310949
return UniqueUnderlyingType;
1090410950

1090510951
return evaluateOrDefault(getASTContext().evaluator,
1090610952
UniqueUnderlyingTypeSubstitutionsRequest{this}, {});
1090710953
}
1090810954

10955+
ArrayRef<OpaqueTypeDecl::ConditionallyAvailableSubstitutions *>
10956+
OpaqueTypeDecl::getConditionallyAvailableSubstitutions() const {
10957+
const_cast<OpaqueTypeDecl *>(this)->loadLazyUnderlyingSubstitutions();
10958+
10959+
assert(ConditionallyAvailableTypes);
10960+
return ConditionallyAvailableTypes.value();
10961+
}
10962+
1090910963
std::optional<unsigned>
1091010964
OpaqueTypeDecl::getAnonymousOpaqueParamOrdinal(TypeRepr *repr) const {
1091110965
assert(NamingDeclAndHasOpaqueReturnTypeRepr.getInt() &&
@@ -10935,7 +10989,8 @@ Identifier OpaqueTypeDecl::getOpaqueReturnTypeIdentifier() const {
1093510989

1093610990
void OpaqueTypeDecl::setConditionallyAvailableSubstitutions(
1093710991
ArrayRef<ConditionallyAvailableSubstitutions *> substitutions) {
10938-
assert(!ConditionallyAvailableTypes &&
10992+
ASSERT(!Bits.OpaqueTypeDecl.HasLazyUnderlyingSubstitutions);
10993+
ASSERT(!ConditionallyAvailableTypes &&
1093910994
"resetting conditionally available substitutions?!");
1094010995
ConditionallyAvailableTypes = getASTContext().AllocateCopy(substitutions);
1094110996
}

lib/ClangImporter/ImporterImpl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1765,6 +1765,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
17651765
llvm_unreachable("unimplemented for ClangImporter");
17661766
}
17671767

1768+
void finishOpaqueTypeDecl(OpaqueTypeDecl *decl,
1769+
uint64_t contextData) override {
1770+
llvm_unreachable("unimplemented for ClangImporter");
1771+
}
1772+
17681773
template <typename DeclTy, typename ...Targs>
17691774
DeclTy *createDeclWithClangNode(ClangNode ClangN, AccessLevel access,
17701775
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)