//===--- PackConformance.cpp - Variadic Protocol Conformance --------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements the PackConformance structure, which describes the // conformance of a type pack parameter to a protocol. // //===----------------------------------------------------------------------===// #include "swift/AST/PackConformance.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/InFlightSubstitution.h" #include "swift/AST/Module.h" #include "swift/AST/Types.h" #include "swift/Basic/Assertions.h" #define DEBUG_TYPE "AST" using namespace swift; void PackConformance::Profile(llvm::FoldingSetNodeID &ID) const { Profile(ID, ConformingType, Protocol, getPatternConformances()); } void PackConformance::Profile(llvm::FoldingSetNodeID &ID, PackType *conformingType, ProtocolDecl *protocol, ArrayRef conformances) { ID.AddPointer(conformingType); ID.AddPointer(protocol); for (auto conformance : conformances) ID.AddPointer(conformance.getOpaqueValue()); } PackConformance::PackConformance(PackType *conformingType, ProtocolDecl *protocol, ArrayRef conformances) : ConformingType(conformingType), Protocol(protocol) { assert(ConformingType->getNumElements() == conformances.size()); std::uninitialized_copy(conformances.begin(), conformances.end(), getTrailingObjects()); } size_t PackConformance::numTrailingObjects( OverloadToken) const { return ConformingType->getNumElements(); } bool PackConformance::isInvalid() const { return llvm::any_of(getPatternConformances(), [&](const auto ref) { return ref.isInvalid(); }); } ArrayRef PackConformance::getPatternConformances() const { return {getTrailingObjects(), ConformingType->getNumElements()}; } bool PackConformance::isCanonical() const { if (!ConformingType->isCanonical()) return false; for (auto conformance : getPatternConformances()) if (!conformance.isCanonical()) return false; return true; } PackConformance *PackConformance::getCanonicalConformance() const { if (isCanonical()) return const_cast(this); SmallVector conformances; for (auto conformance : getPatternConformances()) conformances.push_back(conformance.getCanonicalConformanceRef()); auto canonical = PackConformance::get( cast(ConformingType->getCanonicalType()), Protocol, conformances); assert(canonical->isCanonical()); return canonical; } /// Project the corresponding associated type from each pack element /// of the conforming type, collecting the results into a new pack type /// that has the same pack expansion structure as the conforming type. PackType *PackConformance::getTypeWitness( AssociatedTypeDecl *assocType, SubstOptions options) const { SmallVector packElements; auto conformances = getPatternConformances(); for (unsigned i : indices(conformances)) { auto packElement = ConformingType->getElementType(i); // If the pack element is a pack expansion, build a new pack expansion // with the same count type as the original element, and the pattern // type replaced with the associated type witness from the pattern // conformance. if (auto *packExpansion = packElement->getAs()) { auto assocTypePattern = conformances[i].getTypeWitness(assocType, options); packElements.push_back(PackExpansionType::get( assocTypePattern, packExpansion->getCountType())); // If the pack element is a scalar type, replace the scalar type with // the associated type witness from the pattern conformance. } else { auto assocTypeScalar = conformances[i].getTypeWitness(assocType, options); packElements.push_back(assocTypeScalar); } } return PackType::get(Protocol->getASTContext(), packElements); } /// Project the corresponding associated conformance from each pack element /// of the conforming type, collecting the results into a new pack conformnace /// whose conforming type has the same pack expansion structure as our /// conforming type. PackConformance *PackConformance::getAssociatedConformance( Type assocType, ProtocolDecl *protocol) const { SmallVector packElements; SmallVector packConformances; auto conformances = getPatternConformances(); for (unsigned i : indices(conformances)) { auto packElement = ConformingType->getElementType(i); if (auto *packExpansion = packElement->getAs()) { auto assocConformancePattern = conformances[i].getAssociatedConformance(assocType, protocol); packConformances.push_back(assocConformancePattern); auto assocTypePattern = assocConformancePattern.getType(); packElements.push_back(PackExpansionType::get( assocTypePattern, packExpansion->getCountType())); } else { auto assocConformanceScalar = conformances[i].getAssociatedConformance(assocType, protocol); packConformances.push_back(assocConformanceScalar); auto assocTypeScalar = assocConformanceScalar.getType(); packElements.push_back(assocTypeScalar); } } auto conformingType = PackType::get(Protocol->getASTContext(), packElements); return PackConformance::get(conformingType, protocol, packConformances); } ProtocolConformanceRef PackConformance::subst(SubstitutionMap subMap, SubstOptions options) const { InFlightSubstitutionViaSubMap IFS(subMap, options); return subst(IFS); } ProtocolConformanceRef PackConformance::subst(TypeSubstitutionFn subs, LookupConformanceFn conformances, SubstOptions options) const { InFlightSubstitution IFS(subs, conformances, options); return subst(IFS); } ProtocolConformanceRef PackConformance::subst(InFlightSubstitution &IFS) const { // Results built up by the expansion. SmallVector substElementTypes; SmallVector substConformances; auto origConformances = getPatternConformances(); assert(ConformingType->getNumElements() == origConformances.size()); for (auto i : range(ConformingType->getNumElements())) { auto origElementType = ConformingType->getElementType(i); if (auto *origExpansion = origElementType->getAs()) { // Substitute and expand an expansion element of the original pack. IFS.expandPackExpansionType(origExpansion, [&](Type substComponentType) { substElementTypes.push_back(substComponentType); // Just substitute the conformance. We don't directly represent // pack expansion conformances here; it's sort of implicit in the // corresponding pack element type. substConformances.push_back(origConformances[i].subst(IFS)); }); } else { // Substitute a scalar element of the original pack. substElementTypes.push_back(origElementType.subst(IFS)); substConformances.push_back(origConformances[i].subst(IFS)); } } auto &ctx = Protocol->getASTContext(); auto *substConformingType = PackType::get(ctx, substElementTypes); auto substConformance = PackConformance::get(substConformingType, Protocol, substConformances); return ProtocolConformanceRef(substConformance); } void swift::simple_display(llvm::raw_ostream &out, PackConformance *conformance) { out << conformance->getType() << " : {"; bool first = true; for (auto patternConformance : conformance->getPatternConformances()) { if (first) { out << ", "; first = false; } simple_display(out, patternConformance); } out << "}"; }