//===--- ParameterPack.cpp - Utilities for variadic generics --------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 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 utilities for substituting type parameter packs // appearing in pack expansion types. // //===----------------------------------------------------------------------===// #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/GenericParamList.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "llvm/ADT/SmallVector.h" using namespace swift; namespace { /// Collects all unique pack type parameters referenced from the pattern type, /// skipping those captured by nested pack expansion types. struct PackTypeParameterCollector: TypeWalker { llvm::SetVector typeParams; Action walkToTypePre(Type t) override { if (t->is()) return Action::SkipChildren; if (auto *boundGenericType = dyn_cast(t.getPointer())) { if (auto parentType = boundGenericType->getParent()) parentType.walk(*this); Type(boundGenericType->getExpandedGenericArgsPack()).walk(*this); return Action::SkipChildren; } if (auto *typeAliasType = dyn_cast(t.getPointer())) { if (typeAliasType->getDecl()->isGeneric()) { if (auto parentType = typeAliasType->getParent()) parentType.walk(*this); Type(typeAliasType->getExpandedGenericArgsPack()).walk(*this); return Action::SkipChildren; } } if (auto *paramTy = t->getAs()) { if (paramTy->isParameterPack()) typeParams.insert(paramTy); } else if (auto *archetypeTy = t->getAs()) { typeParams.insert(archetypeTy->getRoot()); } return Action::Continue; } }; } void TypeBase::getTypeParameterPacks( SmallVectorImpl &rootParameterPacks) { PackTypeParameterCollector collector; Type(this).walk(collector); rootParameterPacks.append(collector.typeParams.begin(), collector.typeParams.end()); } bool GenericTypeParamType::isParameterPack() const { if (auto param = getDecl()) { return param->isParameterPack(); } auto fixedNum = ParamOrDepthIndex.get(); return (fixedNum & GenericTypeParamType::TYPE_SEQUENCE_BIT) == GenericTypeParamType::TYPE_SEQUENCE_BIT; } PackType *TypeBase::getPackSubstitutionAsPackType() { if (auto pack = getAs()) { return pack; } else { return PackType::getSingletonPackExpansion(this); } } /// G<{X1, ..., Xn}, {Y1, ..., Yn}>... => {G, ..., G}... PackExpansionType *PackExpansionType::expand() { auto countType = getCountType(); auto *countPack = countType->getAs(); if (countPack == nullptr) return this; auto patternType = getPatternType(); if (patternType->is()) return this; unsigned j = 0; SmallVector expandedTypes; for (auto type : countPack->getElementTypes()) { Type expandedCount; if (auto *expansion = type->getAs()) expandedCount = expansion->getCountType(); auto expandedPattern = patternType.transformRec( [&](Type t) -> Optional { if (t->is()) return t; if (auto *nestedPack = t->getAs()) { auto nestedPackElts = nestedPack->getElementTypes(); if (j < nestedPackElts.size()) { if (expandedCount) { if (auto *expansion = nestedPackElts[j]->getAs()) return expansion->getPatternType(); } else { return nestedPackElts[j]; } } return ErrorType::get(t->getASTContext()); } return None; }); if (expandedCount) { expandedTypes.push_back(PackExpansionType::get(expandedPattern, expandedCount)); } else { expandedTypes.push_back(expandedPattern); } ++j; } auto *packType = PackType::get(getASTContext(), expandedTypes); return PackExpansionType::get(packType, countType); } CanType PackExpansionType::getReducedShape() { auto reducedShape = countType->getReducedShape(); if (reducedShape == getASTContext().TheEmptyTupleType) return reducedShape; return CanType(PackExpansionType::get(reducedShape, reducedShape)); } bool TupleType::containsPackExpansionType() const { for (auto elt : getElements()) { if (elt.getType()->is()) return true; } return false; } bool CanTupleType::containsPackExpansionTypeImpl(CanTupleType tuple) { for (auto eltType : tuple.getElementTypes()) { if (isa(eltType)) return true; } return false; } /// (W, {X, Y}..., Z) => (W, X, Y, Z) Type TupleType::flattenPackTypes() { bool anyChanged = false; SmallVector elts; for (unsigned i = 0, e = getNumElements(); i < e; ++i) { auto elt = getElement(i); if (auto *expansionType = elt.getType()->getAs()) { if (auto *packType = expansionType->getPatternType()->getAs()) { if (!anyChanged) { elts.append(getElements().begin(), getElements().begin() + i); anyChanged = true; } bool first = true; for (auto packElt : packType->getElementTypes()) { if (first) { elts.push_back(TupleTypeElt(packElt, elt.getName())); first = false; continue; } elts.push_back(TupleTypeElt(packElt)); } continue; } } if (anyChanged) elts.push_back(elt); } if (!anyChanged) return this; // If pack substitution yields a single-element tuple, the tuple // structure is flattened to produce the element type. if (elts.size() == 1) { auto type = elts.front().getType(); if (!type->is() && !type->is()) { return type; } } return TupleType::get(elts, getASTContext()); } bool AnyFunctionType::containsPackExpansionType(ArrayRef params) { for (auto param : params) { if (param.getPlainType()->is()) return true; } return false; } /// (W, {X, Y}..., Z) -> T => (W, X, Y, Z) -> T AnyFunctionType *AnyFunctionType::flattenPackTypes() { bool anyChanged = false; SmallVector params; for (unsigned i = 0, e = getParams().size(); i < e; ++i) { auto param = getParams()[i]; if (auto *expansionType = param.getPlainType()->getAs()) { if (auto *packType = expansionType->getPatternType()->getAs()) { if (!anyChanged) { params.append(getParams().begin(), getParams().begin() + i); anyChanged = true; } bool first = true; for (auto packElt : packType->getElementTypes()) { if (first) { params.push_back(param.withType(packElt)); first = false; continue; } params.push_back(param.withType(packElt).getWithoutLabels()); } continue; } } if (anyChanged) params.push_back(param); } if (!anyChanged) return this; if (auto *genericFuncType = getAs()) { return GenericFunctionType::get(genericFuncType->getGenericSignature(), params, getResult(), getExtInfo()); } else { return FunctionType::get(params, getResult(), getExtInfo()); } } bool PackType::containsPackExpansionType() const { for (auto type : getElementTypes()) { if (type->is()) return true; } return false; } /// {W, {X, Y}..., Z} => {W, X, Y, Z} PackType *PackType::flattenPackTypes() { bool anyChanged = false; SmallVector elts; for (unsigned i = 0, e = getNumElements(); i < e; ++i) { auto elt = getElementType(i); if (auto *expansionType = elt->getAs()) { if (auto *packType = expansionType->getPatternType()->getAs()) { if (!anyChanged) { elts.append(getElementTypes().begin(), getElementTypes().begin() + i); anyChanged = true; } for (auto packElt : packType->getElementTypes()) { elts.push_back(packElt); } continue; } } if (anyChanged) elts.push_back(elt); } if (!anyChanged) return this; return PackType::get(getASTContext(), elts); } template static CanPackType getReducedShapeOfPack(const ASTContext &ctx, const T &elementTypes) { SmallVector elts; elts.reserve(elementTypes.size()); for (auto elt : elementTypes) { // T... => shape(T)... if (auto *packExpansionType = elt->template getAs()) { elts.push_back(packExpansionType->getReducedShape()); continue; } // Use () as a placeholder for scalar shape. assert(!elt->template is() && "Pack archetype outside of a pack expansion"); elts.push_back(ctx.TheEmptyTupleType); } return CanPackType(PackType::get(ctx, elts)); } CanPackType PackType::getReducedShape() { return getReducedShapeOfPack(getASTContext(), getElementTypes()); } CanPackType SILPackType::getReducedShape() const { return getReducedShapeOfPack(getASTContext(), getElementTypes()); } CanType TypeBase::getReducedShape() { if (isTypeParameter()) { auto *genericParam = getRootGenericParam(); if (genericParam->isParameterPack()) return genericParam->getCanonicalType(); // Use () as a placeholder for scalar shape. return getASTContext().TheEmptyTupleType; } if (auto *packArchetype = getAs()) return packArchetype->getReducedShape(); if (auto *packType = getAs()) return packType->getReducedShape(); if (auto *expansionType = getAs()) return expansionType->getReducedShape(); if (auto *silPackType = getAs()) { auto can = cast(silPackType->getCanonicalType()); return can->getReducedShape(); } SmallVector rootParameterPacks; getTypeParameterPacks(rootParameterPacks); if (!rootParameterPacks.empty()) return rootParameterPacks.front()->getReducedShape(); assert(!isTypeVariableOrMember()); assert(!hasTypeParameter()); // Use () as a placeholder for scalar shape. return getASTContext().TheEmptyTupleType; } unsigned ParameterList::getOrigParamIndex(SubstitutionMap subMap, unsigned substIndex) const { unsigned remappedIndex = substIndex; for (unsigned i = 0, e = size(); i < e; ++i) { auto *param = get(i); auto paramType = param->getInterfaceType(); unsigned substCount = 1; if (auto *packExpansionType = paramType->getAs()) { auto replacementType = packExpansionType->getCountType().subst(subMap); if (auto *packType = replacementType->getAs()) { substCount = packType->getNumElements(); } } if (remappedIndex < substCount) return i; remappedIndex -= substCount; } llvm::errs() << "Invalid substituted argument index: " << substIndex << "\n"; subMap.dump(llvm::errs()); dump(llvm::errs()); abort(); } /// Foo => Pack{T..., Int, String} PackType *BoundGenericType::getExpandedGenericArgsPack() { // It would be nicer to use genericSig.getInnermostGenericParams() here, // but that triggers a request cycle if we're in the middle of computing // the generic signature already. SmallVector params; for (auto *paramDecl : getDecl()->getGenericParams()->getParams()) { params.push_back(paramDecl->getDeclaredInterfaceType()); } return PackType::get(getASTContext(), TypeArrayView(params), getGenericArgs()); } /// Foo => Pack{T..., Int, String} PackType *TypeAliasType::getExpandedGenericArgsPack() { if (!getDecl()->isGeneric()) return nullptr; auto genericSig = getGenericSignature(); return PackType::get(getASTContext(), genericSig.getInnermostGenericParams(), getDirectGenericArgs()); } /// Pack{T, Pack{Int, String}} => Pack{T..., Int, String} PackType *PackType::get(const ASTContext &C, TypeArrayView params, ArrayRef args) { SmallVector wrappedArgs; assert(params.size() == args.size()); for (unsigned i = 0, e = params.size(); i < e; ++i) { auto arg = args[i]; if (params[i]->isParameterPack()) { auto argPackElements = arg->castTo()->getElementTypes(); wrappedArgs.append(argPackElements.begin(), argPackElements.end()); continue; } wrappedArgs.push_back(arg); } return get(C, wrappedArgs); } PackType *PackType::getSingletonPackExpansion(Type param) { assert(param->isParameterPack() || param->is()); return get(param->getASTContext(), {PackExpansionType::get(param, param)}); } CanPackType CanPackType::getSingletonPackExpansion(CanType param) { return CanPackType(PackType::getSingletonPackExpansion(param)); } bool SILPackType::containsPackExpansionType() const { for (auto type : getElementTypes()) { if (isa(type)) return true; } return false; } CanPackType CanTupleType::getInducedPackTypeImpl(CanTupleType tuple) { return getInducedPackTypeImpl(tuple, 0, tuple->getNumElements()); } CanPackType CanTupleType::getInducedPackTypeImpl(CanTupleType tuple, unsigned start, unsigned count) { assert(start + count <= tuple->getNumElements() && "range out of range"); auto &ctx = tuple->getASTContext(); return CanPackType::get(ctx, tuple.getElementTypes().slice(start, count)); } static CanType getApproximateFormalElementType(const ASTContext &ctx, CanType loweredEltType) { CanType formalEltType = TupleType::getEmpty(ctx); if (auto expansion = dyn_cast(loweredEltType)) formalEltType = CanPackExpansionType::get(formalEltType, expansion.getCountType()); return formalEltType; } template static CanPackType getApproximateFormalPackType(const ASTContext &ctx, Collection loweredEltTypes) { // Build an array of formal element types, but be lazy about it: // use the original array unless we see an element type that doesn't // work as a legal format type. Optional> formalEltTypes; for (auto i : indices(loweredEltTypes)) { auto loweredEltType = loweredEltTypes[i]; bool isLegal = loweredEltType->isLegalFormalType(); // If the type isn't legal as a formal type, substitute the empty // tuple type (or an invariant expansion of it over the count type). CanType formalEltType = loweredEltType; if (!isLegal) { formalEltType = getApproximateFormalElementType(ctx, loweredEltType); } // If we're already building an array, unconditionally append to it. // Otherwise, if the type isn't legal, build the array up to this // point and then append. Otherwise, we're still being lazy. if (formalEltTypes) { formalEltTypes->push_back(formalEltType); } else if (!isLegal) { formalEltTypes.emplace(); formalEltTypes->reserve(loweredEltTypes.size()); formalEltTypes->append(loweredEltTypes.begin(), loweredEltTypes.begin() + i); formalEltTypes->push_back(formalEltType); } assert(isLegal || formalEltTypes.hasValue()); } // Use the array we built if we made one (if we ever saw a non-legal // element type). if (formalEltTypes) { return CanPackType::get(ctx, *formalEltTypes); } else { return CanPackType::get(ctx, loweredEltTypes); } } CanPackType SILPackType::getApproximateFormalPackType() const { return ::getApproximateFormalPackType(getASTContext(), getElementTypes()); } CanPackType CanTupleType::getInducedApproximateFormalPackTypeImpl(CanTupleType tuple) { return ::getApproximateFormalPackType(tuple->getASTContext(), tuple.getElementTypes()); }