Skip to content

Always use PackTypes as the substitutions for type parameter packs #63908

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -6699,6 +6699,10 @@ class PackType final : public TypeBase, public llvm::FoldingSetNode,
TypeArrayView<GenericTypeParamType> params,
ArrayRef<Type> args);

/// Given a pack parameter or pack archetype `T`, produce the
/// pack type `Pack{repeat each T}`.
static PackType *getSingletonPackExpansion(Type packParam);

public:
/// Retrieves the number of elements in this pack.
unsigned getNumElements() const { return Bits.PackType.Count; }
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3302,6 +3302,13 @@ PackType *PackType::getEmpty(const ASTContext &C) {
return cast<PackType>(CanType(C.TheEmptyPackType));
}

PackType *PackType::getSingletonPackExpansion(Type param) {
assert((param->is<GenericTypeParamType>() &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want TypeBase::isParameterPack(), since I'm assuming you want to wrap a dependent member type (each T).Foo in a singleton pack also.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's probably useful

param->castTo<GenericTypeParamType>()->isParameterPack()) ||
(param->is<PackArchetypeType>()));
return get(param->getASTContext(), {PackExpansionType::get(param, param)});
}

CanPackType CanPackType::get(const ASTContext &C, ArrayRef<CanType> elements) {
SmallVector<Type, 8> ncElements(elements.begin(), elements.end());
return CanPackType(PackType::get(C, ncElements));
Expand Down
8 changes: 6 additions & 2 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4657,8 +4657,12 @@ static Type computeNominalType(NominalTypeDecl *decl, DeclTypeKind kind) {
// the generic parameter list directly instead of looking
// at the signature.
SmallVector<Type, 4> args;
for (auto param : decl->getGenericParams()->getParams())
args.push_back(param->getDeclaredInterfaceType());
for (auto param : decl->getGenericParams()->getParams()) {
auto argTy = param->getDeclaredInterfaceType();
if (param->isParameterPack())
argTy = PackType::getSingletonPackExpansion(argTy);
args.push_back(argTy);
}

return BoundGenericType::get(decl, ParentTy, args);
}
Expand Down
29 changes: 28 additions & 1 deletion lib/AST/GenericEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -681,10 +681,37 @@ GenericEnvironment::mapElementTypeIntoPackContext(Type type) const {
}, LookUpConformanceInSignature(sig.getPointer()));
}

namespace {
/// A function suitable for use as a \c TypeSubstitutionFn that produces
/// correct forwarding substitutions for a generic environment.
///
/// This differs from QueryInterfaceTypeSubstitutions only in that it
/// always produces PackTypes for pack parameters.
class BuildForwardingSubstitutions {
QueryInterfaceTypeSubstitutions Query;

public:
BuildForwardingSubstitutions(const GenericEnvironment *self)
: Query(self) { }

Type operator()(SubstitutableType *type) const;
};
} // end anonymous namespace

Type BuildForwardingSubstitutions::operator()(SubstitutableType *type) const {
if (auto resultType = Query(type)) {
auto param = type->castTo<GenericTypeParamType>();
if (!param->isParameterPack())
return resultType;
return PackType::getSingletonPackExpansion(resultType);
}
return Type();
}

SubstitutionMap GenericEnvironment::getForwardingSubstitutionMap() const {
auto genericSig = getGenericSignature();
return SubstitutionMap::get(genericSig,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more efficient implementation would build a SmallVector of archetypes or packs of pack archetypes, and then always call the primitive SubstitutionMap::get(), but I guess this is fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This functor isn't actually used recursively as a substitution function (and we wouldn't generally want to wrap things in packs if it were). It's just initializing a SubstitutionMap and so is called once per generic parameter. Unless it's also used for conformances?

QueryInterfaceTypeSubstitutions(this),
BuildForwardingSubstitutions(this),
MakeAbstractConformanceForGenericType());
}

Expand Down
11 changes: 7 additions & 4 deletions lib/AST/GenericSignature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,10 +566,13 @@ unsigned GenericParamKey::findIndexIn(

SubstitutionMap GenericSignatureImpl::getIdentitySubstitutionMap() const {
return SubstitutionMap::get(const_cast<GenericSignatureImpl *>(this),
[](SubstitutableType *t) -> Type {
return Type(cast<GenericTypeParamType>(t));
},
MakeAbstractConformanceForGenericType());
[](SubstitutableType *t) -> Type {
auto param = cast<GenericTypeParamType>(t);
if (!param->isParameterPack())
return param;
return PackType::getSingletonPackExpansion(param);
},
MakeAbstractConformanceForGenericType());
}

GenericTypeParamType *GenericSignatureImpl::getSugaredType(
Expand Down
5 changes: 5 additions & 0 deletions lib/AST/SubstitutionMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@ SubstitutionMap SubstitutionMap::get(GenericSignature genericSig,

// Record the replacement.
Type replacement = Type(gp).subst(subs, lookupConformance);

assert((!replacement || replacement->hasError() ||
gp->isParameterPack() == replacement->is<PackType>()) &&
"replacement for pack parameter must be a pack type");

replacementTypes.push_back(replacement);
});

Expand Down
21 changes: 21 additions & 0 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4489,6 +4489,24 @@ operator()(CanType dependentType, Type conformingReplacementType,
ProtocolConformanceRef MakeAbstractConformanceForGenericType::
operator()(CanType dependentType, Type conformingReplacementType,
ProtocolDecl *conformedProtocol) const {
// The places that use this can also produce conformance packs, generally
// just for singleton pack expansions.
if (auto conformingPack = conformingReplacementType->getAs<PackType>()) {
SmallVector<ProtocolConformanceRef, 4> conformances;
for (auto conformingPackElt : conformingPack->getElementTypes()) {
// Look through pack expansions; there's no equivalent conformance
// expansion right now.
auto expansion = conformingPackElt->getAs<PackExpansionType>();
if (expansion) conformingPackElt = expansion->getPatternType();

auto conformance =
(*this)(dependentType, conformingPackElt, conformedProtocol);
conformances.push_back(conformance);
}
return ProtocolConformanceRef(
PackConformance::get(conformingPack, conformedProtocol, conformances));
}

assert((conformingReplacementType->is<ErrorType>() ||
conformingReplacementType->is<SubstitutableType>() ||
conformingReplacementType->is<DependentMemberType>() ||
Expand Down Expand Up @@ -4954,6 +4972,9 @@ TypeBase::getContextSubstitutions(const DeclContext *dc,
else if (genericEnv)
substTy = genericEnv->mapTypeIntoContext(gp);

if (gp->isParameterPack() && !substTy->hasError())
substTy = PackType::getSingletonPackExpansion(substTy);

auto result = substitutions.insert(
{gp->getCanonicalType()->castTo<GenericTypeParamType>(),
substTy});
Expand Down
20 changes: 14 additions & 6 deletions lib/IRGen/GenPack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,18 @@ static llvm::Value *bindWitnessTableAtIndex(IRGenFunction &IGF,
return wtable;
}

/// Find the pack archetype for the given interface type in the given
/// opened element context, which is known to be a forwarding context.
static CanPackArchetypeType
getMappedPackArchetypeType(const OpenedElementContext &context, CanType ty) {
auto packType = cast<PackType>(
context.environment->maybeApplyOuterContextSubstitutions(ty)
->getCanonicalType());
auto archetype = getForwardedPackArchetypeType(packType);
assert(archetype);
return archetype;
}

static void bindElementSignatureRequirementsAtIndex(
IRGenFunction &IGF, OpenedElementContext const &context, llvm::Value *index,
DynamicMetadataRequest request) {
Expand All @@ -275,9 +287,7 @@ static void bindElementSignatureRequirementsAtIndex(
break;
case GenericRequirement::Kind::MetadataPack: {
auto ty = requirement.getTypeParameter();
auto patternPackArchetype = cast<PackArchetypeType>(
context.environment->maybeApplyOuterContextSubstitutions(ty)
->getCanonicalType());
auto patternPackArchetype = getMappedPackArchetypeType(context, ty);
auto response =
IGF.emitTypeMetadataRef(patternPackArchetype, request);
auto elementArchetype =
Expand All @@ -295,9 +305,7 @@ static void bindElementSignatureRequirementsAtIndex(
case GenericRequirement::Kind::WitnessTablePack: {
auto ty = requirement.getTypeParameter();
auto proto = requirement.getProtocol();
auto patternPackArchetype = cast<PackArchetypeType>(
context.environment->maybeApplyOuterContextSubstitutions(ty)
->getCanonicalType());
auto patternPackArchetype = getMappedPackArchetypeType(context, ty);
auto elementArchetype =
context.environment
->mapPackTypeIntoElementContext(
Expand Down
8 changes: 6 additions & 2 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,12 @@ Solution::computeSubstitutions(GenericSignature sig,
return SubstitutionMap();

TypeSubstitutionMap subs;
for (const auto &opened : openedTypes->second)
subs[opened.first] = getFixedType(opened.second);
for (const auto &opened : openedTypes->second) {
auto type = getFixedType(opened.second);
if (opened.first->isParameterPack() && !type->is<PackType>())
type = PackType::getSingletonPackExpansion(type);
subs[opened.first] = type;
}

auto lookupConformanceFn =
[&](CanType original, Type replacement,
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/CSBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1483,6 +1483,7 @@ void PotentialBindings::infer(Constraint *constraint) {
auto *elementEnv = CS.getPackElementEnvironment(constraint->getLocator(),
shapeClass);
auto elementType = elementEnv->mapPackTypeIntoElementContext(packType);
assert(!elementType->is<PackType>());
addPotentialBinding({elementType, AllowedBindingKind::Exact, constraint});

break;
Expand Down
18 changes: 18 additions & 0 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8782,6 +8782,19 @@ ConstraintSystem::simplifyBindTupleOfFunctionParamsConstraint(
return SolutionKind::Solved;
}

static Type lookThroughSingletonPackExpansion(Type ty) {
if (auto pack = ty->getAs<PackType>()) {
if (pack->getNumElements() == 1) {
if (auto expansion = pack->getElementType(0)->getAs<PackExpansionType>()) {
auto countType = expansion->getCountType();
if (countType->isEqual(expansion->getPatternType()))
return countType;
}
}
}
return ty;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyPackElementOfConstraint(Type first, Type second,
TypeMatchOptions flags,
Expand All @@ -8801,6 +8814,11 @@ ConstraintSystem::simplifyPackElementOfConstraint(Type first, Type second,
return SolutionKind::Solved;
}

// FIXME: I'm not sure this is actually necessary; I may only be seeing
// this because of something I've screwed up in element generic
// environments.
elementType = lookThroughSingletonPackExpansion(elementType);

// This constraint only exists to vend bindings.
auto *packEnv = DC->getGenericEnvironmentOfContext();
if ((!elementType->hasElementArchetype() && packType->isEqual(elementType)) ||
Expand Down