diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 9775f22a40492..ac2a1a21e7b41 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -761,6 +761,11 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi 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 @@ class OpaqueTypeDecl final : OpaqueTypeDecl(ValueDecl *NamingDecl, GenericParamList *GenericParams, DeclContext *DC, GenericSignature OpaqueInterfaceGenericSignature, - ArrayRef OpaqueReturnTypeReprs); + ArrayRef OpaqueReturnTypeReprs, + bool hasLazyUnderlyingSubstitutions); unsigned getNumOpaqueReturnTypeReprs() const { return NamingDeclAndHasOpaqueReturnTypeRepr.getInt() @@ -3658,13 +3664,21 @@ class OpaqueTypeDecl final : return getNumOpaqueReturnTypeReprs(); } + void loadLazyUnderlyingSubstitutions(); + public: - static OpaqueTypeDecl *get( + static OpaqueTypeDecl *create( ValueDecl *NamingDecl, GenericParamList *GenericParams, DeclContext *DC, GenericSignature OpaqueInterfaceGenericSignature, ArrayRef 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 @@ class OpaqueTypeDecl final : 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(this)->loadLazyUnderlyingSubstitutions(); + return ConditionallyAvailableTypes.has_value(); } ArrayRef - getConditionallyAvailableSubstitutions() const { - assert(ConditionallyAvailableTypes); - return ConditionallyAvailableTypes.value(); - } + getConditionallyAvailableSubstitutions() const; void setConditionallyAvailableSubstitutions( ArrayRef substitutions); diff --git a/include/swift/AST/LazyResolver.h b/include/swift/AST/LazyResolver.h index 1db9f2fd947b9..f86611c70d4d8 100644 --- a/include/swift/AST/LazyResolver.h +++ b/include/swift/AST/LazyResolver.h @@ -63,6 +63,12 @@ class LazyAssociatedTypeData : public LazyContextData { 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 @@ class alignas(void*) LazyMemberLoader { // 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. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 11ee2866f8bfb..2284c615e6f6d 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3174,20 +3174,22 @@ ASTContext::getOrCreateLazyContextData(const Decl *decl, LazyMemberLoader *lazyLoader) { if (auto *data = getLazyContextData(decl)) { // Make sure we didn't provide an incompatible lazy loader. - assert(!lazyLoader || lazyLoader == data->loader); + ASSERT(!lazyLoader || lazyLoader == data->loader); return data; } LazyContextData *&entry = getImpl().LazyContexts[decl]; // Create new lazy context data with the given loader. - assert(lazyLoader && "Queried lazy data for non-lazy iterable context"); + ASSERT(lazyLoader && "Queried lazy data for non-lazy iterable context"); if (isa(decl)) { entry = Allocate(); } else if (isa(decl) || isa(decl)) { entry = Allocate(); + } else if (isa(decl)) { + entry = Allocate(); } else { - assert(isa(decl)); + ASSERT(isa(decl)); entry = Allocate(); } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index ab4a297fd9fbb..61dcdcf8d92d0 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -10820,13 +10820,16 @@ bool AbstractFunctionDecl::isResilient(ModuleDecl *M, OpaqueTypeDecl::OpaqueTypeDecl(ValueDecl *NamingDecl, GenericParamList *GenericParams, DeclContext *DC, GenericSignature OpaqueInterfaceGenericSignature, - ArrayRef - OpaqueReturnTypeReprs) + ArrayRef OpaqueReturnTypeReprs, + bool hasLazyUnderlyingSubstitutions) : GenericTypeDecl(DeclKind::OpaqueType, DC, Identifier(), SourceLoc(), {}, GenericParams), NamingDeclAndHasOpaqueReturnTypeRepr( NamingDecl, !OpaqueReturnTypeReprs.empty()), OpaqueInterfaceGenericSignature(OpaqueInterfaceGenericSignature) { + Bits.OpaqueTypeDecl.HasLazyUnderlyingSubstitutions + = hasLazyUnderlyingSubstitutions; + // Always implicit. setImplicit(); @@ -10839,7 +10842,7 @@ OpaqueTypeDecl::OpaqueTypeDecl(ValueDecl *NamingDecl, OpaqueReturnTypeReprs.end(), getTrailingObjects()); } -OpaqueTypeDecl *OpaqueTypeDecl::get( +OpaqueTypeDecl *OpaqueTypeDecl::create( ValueDecl *NamingDecl, GenericParamList *GenericParams, DeclContext *DC, GenericSignature OpaqueInterfaceGenericSignature, @@ -10850,7 +10853,33 @@ OpaqueTypeDecl *OpaqueTypeDecl::get( auto mem = ctx.Allocate(size, alignof(OpaqueTypeDecl)); return new (mem) OpaqueTypeDecl( NamingDecl, GenericParams, DC, OpaqueInterfaceGenericSignature, - OpaqueReturnTypeReprs); + OpaqueReturnTypeReprs, /*hasLazyUnderlyingSubstitutions=*/false); +} + +OpaqueTypeDecl *OpaqueTypeDecl::createDeserialized( + GenericParamList *GenericParams, DeclContext *DC, + GenericSignature OpaqueInterfaceGenericSignature, + LazyMemberLoader *lazyLoader, uint64_t underlyingSubsData) { + bool hasLazyUnderlyingSubstitutions = (underlyingSubsData != 0); + + ASTContext &ctx = DC->getASTContext(); + auto size = totalSizeToAlloc(0); + auto mem = ctx.Allocate(size, alignof(OpaqueTypeDecl)); + + // NamingDecl is set later by deserialization + auto *decl = new (mem) OpaqueTypeDecl( + /*namingDecl=*/nullptr, GenericParams, DC, + OpaqueInterfaceGenericSignature, { }, + hasLazyUnderlyingSubstitutions); + + if (hasLazyUnderlyingSubstitutions) { + auto &ctx = DC->getASTContext(); + auto *data = static_cast( + ctx.getOrCreateLazyContextData(decl, lazyLoader)); + data->underlyingSubsData = underlyingSubsData; + } + + return decl; } bool OpaqueTypeDecl::isOpaqueReturnTypeOf(const Decl *ownerDecl) const { @@ -10906,9 +10935,26 @@ bool OpaqueTypeDecl::exportUnderlyingType() const { llvm_unreachable("The naming decl is expected to be either an AFD or ASD"); } +void OpaqueTypeDecl::loadLazyUnderlyingSubstitutions() { + if (!Bits.OpaqueTypeDecl.HasLazyUnderlyingSubstitutions) + return; + + Bits.OpaqueTypeDecl.HasLazyUnderlyingSubstitutions = 0; + + auto &ctx = getASTContext(); + auto *data = static_cast( + ctx.getLazyContextData(this)); + ASSERT(data != nullptr); + + data->loader->finishOpaqueTypeDecl( + this, data->underlyingSubsData); +} + std::optional OpaqueTypeDecl::getUniqueUnderlyingTypeSubstitutions( bool typeCheckFunctionBodies) const { + const_cast(this)->loadLazyUnderlyingSubstitutions(); + if (!typeCheckFunctionBodies) return UniqueUnderlyingType; @@ -10916,6 +10962,14 @@ OpaqueTypeDecl::getUniqueUnderlyingTypeSubstitutions( UniqueUnderlyingTypeSubstitutionsRequest{this}, {}); } +ArrayRef +OpaqueTypeDecl::getConditionallyAvailableSubstitutions() const { + const_cast(this)->loadLazyUnderlyingSubstitutions(); + + assert(ConditionallyAvailableTypes); + return ConditionallyAvailableTypes.value(); +} + std::optional OpaqueTypeDecl::getAnonymousOpaqueParamOrdinal(TypeRepr *repr) const { assert(NamingDeclAndHasOpaqueReturnTypeRepr.getInt() && @@ -10945,7 +10999,8 @@ Identifier OpaqueTypeDecl::getOpaqueReturnTypeIdentifier() const { void OpaqueTypeDecl::setConditionallyAvailableSubstitutions( ArrayRef substitutions) { - assert(!ConditionallyAvailableTypes && + ASSERT(!Bits.OpaqueTypeDecl.HasLazyUnderlyingSubstitutions); + ASSERT(!ConditionallyAvailableTypes && "resetting conditionally available substitutions?!"); ConditionallyAvailableTypes = getASTContext().AllocateCopy(substitutions); } diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 1ce1ff5981b14..deb2e08723e94 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -1762,6 +1762,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation llvm_unreachable("unimplemented for ClangImporter"); } + void finishOpaqueTypeDecl(OpaqueTypeDecl *decl, + uint64_t contextData) override { + llvm_unreachable("unimplemented for ClangImporter"); + } + template DeclTy *createDeclWithClangNode(ClangNode ClangN, AccessLevel access, Targs &&... Args) { diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index 2a51bb2253380..92cfd9d19f80a 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -215,7 +215,7 @@ OpaqueResultTypeRequest::evaluate(Evaluator &evaluator, } // Create the OpaqueTypeDecl for the result type. - auto opaqueDecl = OpaqueTypeDecl::get( + auto opaqueDecl = OpaqueTypeDecl::create( originatingDecl, genericParams, parentDC, interfaceSignature, opaqueReprs); if (auto originatingSig = originatingDC->getGenericSignatureOfContext()) { diff --git a/lib/Serialization/DeclTypeRecordNodes.def b/lib/Serialization/DeclTypeRecordNodes.def index 0ee58b5d3e668..d1462d53cd735 100644 --- a/lib/Serialization/DeclTypeRecordNodes.def +++ b/lib/Serialization/DeclTypeRecordNodes.def @@ -215,10 +215,11 @@ OTHER(ERROR_FLAG, 155) OTHER(ABI_ONLY_COUNTERPART, 156) OTHER(DECL_NAME_REF, 157) +TRAILING_INFO(UNDERLYING_SUBSTITUTION) TRAILING_INFO(CONDITIONAL_SUBSTITUTION) TRAILING_INFO(CONDITIONAL_SUBSTITUTION_COND) -OTHER(LIFETIME_DEPENDENCE, 160) +OTHER(LIFETIME_DEPENDENCE, 161) TRAILING_INFO(INHERITED_PROTOCOLS) #ifndef DECL_ATTR diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index c92bbead37038..e5796a69ca66d 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -73,6 +73,8 @@ STATISTIC(NumNormalProtocolConformancesLoaded, "# of normal protocol conformances deserialized"); STATISTIC(NumNormalProtocolConformancesCompleted, "# of normal protocol conformances completed"); +STATISTIC(NumOpaqueTypeDeclsCompleted, + "# of opaque type declarations completed"); STATISTIC(NumNestedTypeShortcuts, "# of nested types resolved without full lookup"); @@ -4615,84 +4617,6 @@ class DeclDeserializer { return deserializeAnyFunc(scratch, blobData, /*isAccessor*/true); } - void deserializeConditionalSubstitutionAvailabilityQueries( - SmallVectorImpl &queries) { - using namespace decls_block; - - SmallVector scratch; - StringRef blobData; - - // FIXME: [availability] Support arbitrary domains (rdar://156513787). - auto domain = ctx.getTargetAvailabilityDomain(); - ASSERT(domain.isPlatform()); - - while (true) { - llvm::BitstreamEntry entry = - MF.fatalIfUnexpected(MF.DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); - if (entry.Kind != llvm::BitstreamEntry::Record) - break; - - scratch.clear(); - - unsigned recordID = MF.fatalIfUnexpected( - MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); - if (recordID != decls_block::CONDITIONAL_SUBSTITUTION_COND) - break; - - bool isUnavailability; - DEF_VER_TUPLE_PIECES(version); - - ConditionalSubstitutionConditionLayout::readRecord( - scratch, isUnavailability, LIST_VER_TUPLE_PIECES(version)); - - llvm::VersionTuple version; - DECODE_VER_TUPLE(version); - - queries.push_back(AvailabilityQuery::dynamic( - domain, AvailabilityRange(version), std::nullopt) - .asUnavailable(isUnavailability)); - } - } - - void deserializeConditionalSubstitutions( - SmallVectorImpl - &limitedAvailability) { - SmallVector scratch; - StringRef blobData; - - while (true) { - llvm::BitstreamEntry entry = - MF.fatalIfUnexpected(MF.DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); - if (entry.Kind != llvm::BitstreamEntry::Record) - break; - - scratch.clear(); - unsigned recordID = MF.fatalIfUnexpected( - MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); - if (recordID != decls_block::CONDITIONAL_SUBSTITUTION) - break; - - SubstitutionMapID substitutionMapRef; - - decls_block::ConditionalSubstitutionLayout::readRecord( - scratch, substitutionMapRef); - - SmallVector queries; - deserializeConditionalSubstitutionAvailabilityQueries(queries); - - if (queries.empty()) - return MF.diagnoseAndConsumeFatal(); - - auto subMapOrError = MF.getSubstitutionMapChecked(substitutionMapRef); - if (!subMapOrError) - return MF.diagnoseAndConsumeFatal(); - - limitedAvailability.push_back( - OpaqueTypeDecl::ConditionallyAvailableSubstitutions::get( - ctx, queries, subMapOrError.get())); - } - } - Expected deserializeOpaqueType(ArrayRef scratch, StringRef blobData) { DeclID namingDeclID; @@ -4700,16 +4624,16 @@ class DeclDeserializer { GenericSignatureID interfaceSigID; TypeID interfaceTypeID; GenericSignatureID genericSigID; - SubstitutionMapID underlyingTypeSubsID; uint8_t rawAccessLevel; + bool hasUnderlyingType; bool exportUnderlyingType; decls_block::OpaqueTypeLayout::readRecord(scratch, contextID, namingDeclID, interfaceSigID, interfaceTypeID, genericSigID, - underlyingTypeSubsID, rawAccessLevel, + hasUnderlyingType, exportUnderlyingType); - + DeclContext *declContext; SET_OR_RETURN_ERROR(declContext, MF.getDeclContextChecked(contextID)); @@ -4724,15 +4648,36 @@ class DeclDeserializer { GenericParamList *genericParams; SET_OR_RETURN_ERROR(genericParams, MF.maybeReadGenericParams(declContext)); + // FIXME: Do we still need exportUnderlyingType? + uint64_t contextData = 0; + if (hasUnderlyingType && + (exportUnderlyingType || + MF.FileContext->getParentModule()->isMainModule())) { + contextData = MF.DeclTypeCursor.GetCurrentBitNo(); + } + // Create the decl. - auto opaqueDecl = OpaqueTypeDecl::get( - /*NamingDecl=*/ nullptr, genericParams, declContext, - interfaceSigOrErr.get(), /*OpaqueReturnTypeReprs*/ { }); + auto opaqueDecl = OpaqueTypeDecl::createDeserialized( + genericParams, declContext, interfaceSigOrErr.get(), + &MF, contextData); declOrOffset = opaqueDecl; auto namingDecl = cast(MF.getDecl(namingDeclID)); opaqueDecl->setNamingDecl(namingDecl); + if (contextData == 0) { + LLVM_DEBUG( + llvm::dbgs() << "Ignoring underlying information for opaque type of '"; + llvm::dbgs() << namingDecl->getName(); + llvm::dbgs() << "'\n"); + } else { + LLVM_DEBUG( + llvm::dbgs() << "Loading underlying information for opaque type of '"; + llvm::dbgs() << namingDecl->getName(); + llvm::dbgs() << "'\n"; + ); + } + auto interfaceType = MF.getType(interfaceTypeID); opaqueDecl->setInterfaceType(MetatypeType::get(interfaceType)); @@ -4747,55 +4692,6 @@ class DeclDeserializer { else opaqueDecl->setGenericSignature(GenericSignature()); - if (!MF.FileContext->getParentModule()->isMainModule() && - !exportUnderlyingType) { - // Do not try to read the underlying type information if the function - // is not inlinable in clients. This reflects the swiftinterface behavior - // in where clients are only aware of the underlying type when the body - // of the function is public. - LLVM_DEBUG( - llvm::dbgs() << "Ignoring underlying information for opaque type of '"; - llvm::dbgs() << namingDecl->getName(); - llvm::dbgs() << "'\n"; - ); - - } else if (underlyingTypeSubsID) { - LLVM_DEBUG( - llvm::dbgs() << "Loading underlying information for opaque type of '"; - llvm::dbgs() << namingDecl->getName(); - llvm::dbgs() << "'\n"; - ); - - auto subMapOrError = MF.getSubstitutionMapChecked(underlyingTypeSubsID); - if (!subMapOrError) { - // If the underlying type references internal details, ignore it. - auto unconsumedError = - MF.consumeExpectedError(subMapOrError.takeError()); - if (unconsumedError) - return std::move(unconsumedError); - } else { - // Check whether there are any conditionally available substitutions. - // If there are, it means that "unique" we just read is a universally - // available substitution. - SmallVector - limitedAvailability; - - deserializeConditionalSubstitutions(limitedAvailability); - - if (limitedAvailability.empty()) { - opaqueDecl->setUniqueUnderlyingTypeSubstitutions(subMapOrError.get()); - } else { - limitedAvailability.push_back( - OpaqueTypeDecl::ConditionallyAvailableSubstitutions::get( - ctx, - {AvailabilityQuery::universallyConstant( - /*value=*/true)}, - subMapOrError.get())); - - opaqueDecl->setConditionallyAvailableSubstitutions(limitedAvailability); - } - } - } return opaqueDecl; } @@ -8902,6 +8798,148 @@ ModuleFile::loadAssociatedTypeDefault(const swift::AssociatedTypeDecl *ATD, return getType(contextData); } +void +ModuleFile::finishOpaqueTypeDecl(OpaqueTypeDecl *opaqueDecl, + uint64_t contextData) { + auto &ctx = getAssociatedModule()->getASTContext(); + auto *namingDecl = opaqueDecl->getNamingDecl(); + + using namespace decls_block; + PrettyStackTraceModuleFile traceModule(*this); + PrettyStackTraceDecl trace("finishing opaque result type ", namingDecl); + ++NumOpaqueTypeDeclsCompleted; + + // Find the underlying type substitutions record. + BCOffsetRAII restoreOffset(DeclTypeCursor); + if (diagnoseAndConsumeFatalIfNotSuccess( + DeclTypeCursor.JumpToBit(contextData))) + return; + llvm::BitstreamEntry entry = fatalIfUnexpected(DeclTypeCursor.advance()); + assert(entry.Kind == llvm::BitstreamEntry::Record && + "registered lazy loader incorrectly"); + + SubstitutionMapID underlyingTypeSubsID; + SmallVector scratch; + + unsigned kind = + fatalIfUnexpected(DeclTypeCursor.readRecord(entry.ID, scratch)); + if (kind != UNDERLYING_SUBSTITUTION) + fatal(llvm::make_error(kind, + "registered lazy loader incorrectly")); + + UnderlyingSubstitutionLayout::readRecord(scratch, underlyingTypeSubsID); + + auto subMapOrError = getSubstitutionMapChecked(underlyingTypeSubsID); + if (!subMapOrError) { + // If the underlying type references internal details, ignore it. + diagnoseAndConsumeError(subMapOrError.takeError()); + return; + } + + // Check whether there are any conditionally available substitutions. + // If there are, it means that "unique" we just read is a universally + // available substitution. + SmallVector + limitedAvailability; + + deserializeConditionalSubstitutions(limitedAvailability); + + if (limitedAvailability.empty()) { + opaqueDecl->setUniqueUnderlyingTypeSubstitutions(subMapOrError.get()); + } else { + limitedAvailability.push_back( + OpaqueTypeDecl::ConditionallyAvailableSubstitutions::get( + ctx, + {AvailabilityQuery::universallyConstant( + /*value=*/true)}, + subMapOrError.get())); + + opaqueDecl->setConditionallyAvailableSubstitutions(limitedAvailability); + } +} + +void ModuleFile::deserializeConditionalSubstitutions( + SmallVectorImpl + &limitedAvailability) { + auto &ctx = getAssociatedModule()->getASTContext(); + + SmallVector scratch; + StringRef blobData; + + while (true) { + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); + if (entry.Kind != llvm::BitstreamEntry::Record) + break; + + scratch.clear(); + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); + if (recordID != decls_block::CONDITIONAL_SUBSTITUTION) + break; + + SubstitutionMapID substitutionMapRef; + + decls_block::ConditionalSubstitutionLayout::readRecord( + scratch, substitutionMapRef); + + SmallVector queries; + deserializeConditionalSubstitutionAvailabilityQueries(queries); + + if (queries.empty()) + return diagnoseAndConsumeFatal(); + + auto subMapOrError = getSubstitutionMapChecked(substitutionMapRef); + if (!subMapOrError) + return diagnoseAndConsumeFatal(); + + limitedAvailability.push_back( + OpaqueTypeDecl::ConditionallyAvailableSubstitutions::get( + ctx, queries, subMapOrError.get())); + } +} + +void ModuleFile::deserializeConditionalSubstitutionAvailabilityQueries( + SmallVectorImpl &queries) { + using namespace decls_block; + + auto &ctx = getAssociatedModule()->getASTContext(); + + SmallVector scratch; + StringRef blobData; + + // FIXME: [availability] Support arbitrary domains (rdar://156513787). + auto domain = ctx.getTargetAvailabilityDomain(); + ASSERT(domain.isPlatform()); + + while (true) { + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance(AF_DontPopBlockAtEnd)); + if (entry.Kind != llvm::BitstreamEntry::Record) + break; + + scratch.clear(); + + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); + if (recordID != decls_block::CONDITIONAL_SUBSTITUTION_COND) + break; + + bool isUnavailability; + DEF_VER_TUPLE_PIECES(version); + + ConditionalSubstitutionConditionLayout::readRecord( + scratch, isUnavailability, LIST_VER_TUPLE_PIECES(version)); + + llvm::VersionTuple version; + DECODE_VER_TUPLE(version); + + queries.push_back(AvailabilityQuery::dynamic( + domain, AvailabilityRange(version), std::nullopt) + .asUnavailable(isUnavailability)); + } +} + ValueDecl *ModuleFile::loadDynamicallyReplacedFunctionDecl( const DynamicReplacementAttr *DRA, uint64_t contextData) { return cast(getDecl(contextData)); diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index 28bb4da5104fd..bb4e7edcf33bd 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -951,21 +951,31 @@ class ModuleFile virtual void finishNormalConformance(NormalProtocolConformance *conformance, uint64_t contextData) override; - void + virtual void loadRequirementSignature(const ProtocolDecl *proto, uint64_t contextData, SmallVectorImpl &requirements, SmallVectorImpl &typeAliases) override; - void + virtual void loadAssociatedTypes( const ProtocolDecl *proto, uint64_t contextData, SmallVectorImpl &assocTypes) override; - void + virtual void loadPrimaryAssociatedTypes( const ProtocolDecl *proto, uint64_t contextData, SmallVectorImpl &assocTypes) override; + virtual void + finishOpaqueTypeDecl(OpaqueTypeDecl *opaqueDecl, + uint64_t contextData) override; + + void deserializeConditionalSubstitutions( + SmallVectorImpl + &limitedAvailability); + void deserializeConditionalSubstitutionAvailabilityQueries( + SmallVectorImpl &queries); + std::optional getGroupNameById(unsigned Id) const; std::optional getSourceFileNameById(unsigned Id) const; std::optional getGroupNameForDecl(const Decl *D) const; diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index fbfee7a5baaf7..1e8ac35904c87 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 974; // remove 'isCallerIsolated' bit from ParamDecl +const uint16_t SWIFTMODULE_VERSION_MINOR = 975; // Lazy OpaqueTypeDecl underlying type substitutions /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1807,6 +1807,26 @@ namespace decls_block { // - inlinable body text, if any >; + using OpaqueTypeLayout = BCRecordLayout< + OPAQUE_TYPE_DECL, + DeclContextIDField, // decl context + DeclIDField, // naming decl + GenericSignatureIDField, // interface generic signature + TypeIDField, // interface type for opaque type + GenericSignatureIDField, // generic environment + AccessLevelField, // access level + BCFixed<1>, // has underlying substitutions? + BCFixed<1> // export underlying substitutions? + // trailed by generic parameters + // trailed by opaque type underlying substitutions (if has underlying substitutions) + // trailed by conditional substitutions (if has underlying substitutions) + >; + + using UnderlyingSubstitutionLayout = BCRecordLayout< + UNDERLYING_SUBSTITUTION, + SubstitutionMapIDField // the substitution map + >; + using ConditionalSubstitutionConditionLayout = BCRecordLayout< CONDITIONAL_SUBSTITUTION_COND, BCFixed<1>, // is unavailable? @@ -1820,20 +1840,6 @@ namespace decls_block { // unavailability indicator. >; - using OpaqueTypeLayout = BCRecordLayout< - OPAQUE_TYPE_DECL, - DeclContextIDField, // decl context - DeclIDField, // naming decl - GenericSignatureIDField, // interface generic signature - TypeIDField, // interface type for opaque type - GenericSignatureIDField, // generic environment - SubstitutionMapIDField, // optional substitution map for underlying type - AccessLevelField, // access level - BCFixed<1> // export underlying type details - // trailed by generic parameters - // trailed by conditional substitutions - >; - // TODO: remove the unnecessary FuncDecl components here using AccessorLayout = BCRecordLayout< ACCESSOR_DECL, diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index fb7a94dd56588..0e4a79478a692 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -4915,41 +4915,50 @@ class Serializer::DeclSerializer : public DeclVisitor { auto genericSigID = S.addGenericSignatureRef(opaqueDecl->getGenericSignature()); + uint8_t rawAccessLevel = + getRawStableAccessLevel(opaqueDecl->getFormalAccess()); + bool exportUnderlyingType = opaqueDecl->exportUnderlyingType(); + + bool hasUnderlyingType = false; SubstitutionMapID underlyingSubsID = 0; if (auto underlying = opaqueDecl->getUniqueUnderlyingTypeSubstitutions()) { underlyingSubsID = S.addSubstitutionMapRef(*underlying); + hasUnderlyingType = true; } else if (opaqueDecl->hasConditionallyAvailableSubstitutions()) { // Universally available type doesn't have any availability conditions - // so it could be serialized into "unique" slot to safe space. + // so it could be serialized into "unique" slot to save space. auto universal = opaqueDecl->getConditionallyAvailableSubstitutions().back(); underlyingSubsID = S.addSubstitutionMapRef(universal->getSubstitutions()); + hasUnderlyingType = true; } - uint8_t rawAccessLevel = - getRawStableAccessLevel(opaqueDecl->getFormalAccess()); - bool exportDetails = opaqueDecl->exportUnderlyingType(); - unsigned abbrCode = S.DeclTypeAbbrCodes[OpaqueTypeLayout::Code]; - OpaqueTypeLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, + OpaqueTypeLayout::emitRecord(S.Out, S.ScratchRecord, + S.DeclTypeAbbrCodes[OpaqueTypeLayout::Code], contextID.getOpaqueValue(), namingDeclID, interfaceSigID, interfaceTypeID, genericSigID, - underlyingSubsID, rawAccessLevel, - exportDetails); + rawAccessLevel, hasUnderlyingType, + exportUnderlyingType); writeGenericParams(opaqueDecl->getGenericParams()); + if (!hasUnderlyingType) + return; + + UnderlyingSubstitutionLayout::emitRecord( + S.Out, S.ScratchRecord, + S.DeclTypeAbbrCodes[UnderlyingSubstitutionLayout::Code], + underlyingSubsID); + // Serialize all of the conditionally available substitutions expect the // last one - universal, it's serialized into "unique" slot. if (opaqueDecl->hasConditionallyAvailableSubstitutions()) { - unsigned abbrCode = - S.DeclTypeAbbrCodes[ConditionalSubstitutionLayout::Code]; for (const auto *subs : opaqueDecl->getConditionallyAvailableSubstitutions().drop_back()) { ConditionalSubstitutionLayout::emitRecord( - S.Out, S.ScratchRecord, abbrCode, + S.Out, S.ScratchRecord, + S.DeclTypeAbbrCodes[ConditionalSubstitutionLayout::Code], S.addSubstitutionMapRef(subs->getSubstitutions())); - unsigned condAbbrCode = - S.DeclTypeAbbrCodes[ConditionalSubstitutionConditionLayout::Code]; for (const auto &query : subs->getAvailabilityQueries()) { // FIXME: [availability] Support arbitrary domains (rdar://156513787). DEBUG_ASSERT(query.getDomain().isPlatform()); @@ -4961,8 +4970,9 @@ class Serializer::DeclSerializer : public DeclVisitor { std::optional( availableRange.getRawMinimumVersion())); ConditionalSubstitutionConditionLayout::emitRecord( - S.Out, S.ScratchRecord, condAbbrCode, query.isUnavailability(), - LIST_VER_TUPLE_PIECES(osVersion)); + S.Out, S.ScratchRecord, + S.DeclTypeAbbrCodes[ConditionalSubstitutionConditionLayout::Code], + query.isUnavailability(), LIST_VER_TUPLE_PIECES(osVersion)); } } } @@ -6559,6 +6569,7 @@ void Serializer::writeAllDeclsAndTypes() { registerDeclTypeAbbr(); registerDeclTypeAbbr(); + registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); diff --git a/test/Serialization/Inputs/opaque_circularity_swiftui_other.swift b/test/Serialization/Inputs/opaque_circularity_swiftui_other.swift new file mode 100644 index 0000000000000..4cc9f9fa61566 --- /dev/null +++ b/test/Serialization/Inputs/opaque_circularity_swiftui_other.swift @@ -0,0 +1,15 @@ +import SwiftUI + +public struct OnOffColor { + public var on: any ShapeStyle { fatalError() } + + public func resolve() -> some ShapeStyle { + AnyShapeStyle(on) + } +} + +extension View { + @inlinable public func foregroundStyle(_ style: OnOffColor, isOn: Bool) -> some View { + foregroundStyle(style.resolve()) + } +} diff --git a/test/Serialization/opaque_circularity_swiftui.swift b/test/Serialization/opaque_circularity_swiftui.swift new file mode 100644 index 0000000000000..bba111139a14c --- /dev/null +++ b/test/Serialization/opaque_circularity_swiftui.swift @@ -0,0 +1,12 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module %S/Inputs/opaque_circularity_swiftui_other.swift -emit-module -emit-module-path %t/opaque_circularity_swiftui_other.swiftmodule -target %target-cpu-apple-macosx12 -swift-version 5 +// RUN: %target-swift-frontend -emit-silgen %s -I %t -target %target-cpu-apple-macosx12 -swift-version 5 + +// REQUIRES: OS=macosx + +import opaque_circularity_swiftui_other +import SwiftUI + +func foo(_ v: some View, _ tint: Color) { + _ = v.foregroundStyle(tint) +}