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.
This commit is contained in:
Slava Pestov
2025-11-18 21:32:58 -05:00
parent cf535d8b99
commit 27097430cc
13 changed files with 362 additions and 183 deletions

View File

@@ -761,6 +761,11 @@ protected:
HasAnyUnavailableDuringLoweringValues : 1
);
SWIFT_INLINE_BITFIELD(OpaqueTypeDecl, GenericTypeDecl, 1,
/// Whether we have lazily-loaded underlying substitutions.
HasLazyUnderlyingSubstitutions : 1
);
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,
/// If the module is compiled as static library.
StaticLibrary : 1,
@@ -3646,7 +3651,8 @@ private:
OpaqueTypeDecl(ValueDecl *NamingDecl, GenericParamList *GenericParams,
DeclContext *DC,
GenericSignature OpaqueInterfaceGenericSignature,
ArrayRef<TypeRepr *> OpaqueReturnTypeReprs);
ArrayRef<TypeRepr *> OpaqueReturnTypeReprs,
bool hasLazyUnderlyingSubstitutions);
unsigned getNumOpaqueReturnTypeReprs() const {
return NamingDeclAndHasOpaqueReturnTypeRepr.getInt()
@@ -3658,13 +3664,21 @@ private:
return getNumOpaqueReturnTypeReprs();
}
void loadLazyUnderlyingSubstitutions();
public:
static OpaqueTypeDecl *get(
static OpaqueTypeDecl *create(
ValueDecl *NamingDecl, GenericParamList *GenericParams,
DeclContext *DC,
GenericSignature OpaqueInterfaceGenericSignature,
ArrayRef<TypeRepr *> OpaqueReturnTypeReprs);
static OpaqueTypeDecl *createDeserialized(
GenericParamList *GenericParams,
DeclContext *DC,
GenericSignature OpaqueInterfaceGenericSignature,
LazyMemberLoader *lazyLoader, uint64_t underlyingSubsData);
ValueDecl *getNamingDecl() const {
return NamingDeclAndHasOpaqueReturnTypeRepr.getPointer();
}
@@ -3720,19 +3734,19 @@ public:
bool typeCheckFunctionBodies=true) const;
void setUniqueUnderlyingTypeSubstitutions(SubstitutionMap subs) {
assert(!UniqueUnderlyingType.has_value() && "resetting underlying type?!");
ASSERT(!Bits.OpaqueTypeDecl.HasLazyUnderlyingSubstitutions);
ASSERT(!UniqueUnderlyingType.has_value() && "resetting underlying type?!");
UniqueUnderlyingType = subs;
}
bool hasConditionallyAvailableSubstitutions() const {
const_cast<OpaqueTypeDecl *>(this)->loadLazyUnderlyingSubstitutions();
return ConditionallyAvailableTypes.has_value();
}
ArrayRef<ConditionallyAvailableSubstitutions *>
getConditionallyAvailableSubstitutions() const {
assert(ConditionallyAvailableTypes);
return ConditionallyAvailableTypes.value();
}
getConditionallyAvailableSubstitutions() const;
void setConditionallyAvailableSubstitutions(
ArrayRef<ConditionallyAvailableSubstitutions *> substitutions);

View File

@@ -63,6 +63,12 @@ public:
uint64_t defaultDefinitionTypeData = 0;
};
class LazyOpaqueTypeData : public LazyContextData {
public:
/// The context data used for loading the underlying type substitution map.
uint64_t underlyingSubsData = 0;
};
/// Context data for protocols.
class LazyProtocolData : public LazyIterableDeclContextData {
public:
@@ -138,6 +144,10 @@ public:
// Returns the target parameter of the `@_specialize` attribute or null.
virtual ValueDecl *loadTargetFunctionDecl(const AbstractSpecializeAttr *attr,
uint64_t contextData) = 0;
/// Loads the underlying type substitution map of an opaque result declaration.
virtual void
finishOpaqueTypeDecl(OpaqueTypeDecl *opaqueDecl, uint64_t contextData) = 0;
};
/// A class that can lazily load conformances from a serialized format.