Skip to content

Commit 8092f18

Browse files
committed
AST: Generalize GenericSignatureImpl::getUpperBound()
Handle inverses, and add a couple of flags (yuck!) to allow its usage in diagnostics and code completion.
1 parent 57b768d commit 8092f18

File tree

4 files changed

+80
-32
lines changed

4 files changed

+80
-32
lines changed

include/swift/AST/GenericSignature.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,9 +465,22 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final
465465
/// Given a type parameter, compute the most specific supertype (upper bound),
466466
/// possibly dependent on other type parameters.
467467
///
468+
///
469+
/// \param forExistentialSelf If true, we ensure the result does not include
470+
/// any type parameters rooted in the same generic parameter as the one given.
471+
///
472+
/// \param includeParameterizedProtocols If true, we form parameterized
473+
/// protocol types if we find that the given type's primary associated types
474+
/// are sufficiently constrained.
475+
///
468476
/// \note If the upper bound is a protocol or protocol composition,
469477
/// will return an instance of \c ExistentialType.
470-
Type getUpperBound(Type type) const;
478+
Type getUpperBound(Type type,
479+
bool forExistentialSelf,
480+
bool includeParameterizedProtocols) const;
481+
482+
/// Utility wrapper for use when this is an opened existential signature.
483+
Type getExistentialType(Type type) const;
471484

472485
static void Profile(llvm::FoldingSetNodeID &ID,
473486
ArrayRef<GenericTypeParamType *> genericParams,

lib/AST/GenericSignature.cpp

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -667,58 +667,91 @@ unsigned GenericSignatureImpl::getGenericParamOrdinal(
667667
return GenericParamKey(param).findIndexIn(getGenericParams());
668668
}
669669

670-
Type GenericSignatureImpl::getUpperBound(Type type) const {
670+
Type GenericSignatureImpl::getUpperBound(Type type,
671+
bool forExistentialSelf,
672+
bool includeParameterizedProtocols) const {
671673
assert(type->isTypeParameter());
672674

673675
llvm::SmallVector<Type, 2> types;
674676
unsigned rootDepth = type->getRootGenericParam()->getDepth();
675677

678+
auto accept = [forExistentialSelf, rootDepth](Type t) {
679+
if (!forExistentialSelf)
680+
return true;
681+
682+
return !t.findIf([rootDepth](Type t) {
683+
if (auto *paramTy = t->getAs<GenericTypeParamType>())
684+
return (paramTy->getDepth() == rootDepth);
685+
return false;
686+
});
687+
};
688+
689+
// We start with the assumption we'll add a '& AnyObject' member to our
690+
// composition, but we might clear this below.
676691
bool hasExplicitAnyObject = requiresClass(type);
677692

678-
if (Type superclass = getSuperclassBound(type)) {
693+
// Look for the most derived superclass that does not involve the type
694+
// being erased.
695+
Type superclass = getSuperclassBound(type);
696+
if (superclass) {
679697
do {
680698
superclass = getReducedType(superclass);
681-
682-
if (!superclass.findIf([&](Type t) {
683-
if (auto *paramTy = t->getAs<GenericTypeParamType>())
684-
return (paramTy->getDepth() == rootDepth);
685-
return false;
686-
})) {
699+
if (accept(superclass))
687700
break;
688-
}
689701
} while ((superclass = superclass->getSuperclass()));
690702

703+
// If we're going to have a superclass, we can drop the '& AnyObject'.
691704
if (superclass) {
692-
types.push_back(superclass);
705+
types.push_back(getSugaredType(superclass));
693706
hasExplicitAnyObject = false;
694707
}
695708
}
696709

710+
auto &ctx = getASTContext();
711+
712+
// Record the absence of Copyable and Escapable conformance, but only if
713+
// we didn't have a superclass or require AnyObject.
714+
InvertibleProtocolSet inverses;
715+
716+
if (SWIFT_ENABLE_EXPERIMENTAL_NONCOPYABLE_GENERICS ||
717+
ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
718+
if (!superclass && !hasExplicitAnyObject) {
719+
for (auto ip : InvertibleProtocolSet::full()) {
720+
auto *kp = ctx.getProtocol(::getKnownProtocolKind(ip));
721+
if (!requiresProtocol(type, kp))
722+
inverses.insert(ip);
723+
}
724+
}
725+
}
726+
697727
for (auto *proto : getRequiredProtocols(type)) {
728+
if (SWIFT_ENABLE_EXPERIMENTAL_NONCOPYABLE_GENERICS ||
729+
ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
730+
// Don't add invertible protocols to the composition, because we recorded
731+
// their absence above.
732+
if (proto->getInvertibleProtocolKind())
733+
continue;
734+
}
735+
698736
if (proto->requiresClass())
699737
hasExplicitAnyObject = false;
700738

701739
auto *baseType = proto->getDeclaredInterfaceType()->castTo<ProtocolType>();
702740

703741
auto primaryAssocTypes = proto->getPrimaryAssociatedTypes();
704-
if (!primaryAssocTypes.empty()) {
742+
if (includeParameterizedProtocols && !primaryAssocTypes.empty()) {
705743
SmallVector<Type, 2> argTypes;
706744

707745
// Attempt to recover same-type requirements on primary associated types.
708746
for (auto *assocType : primaryAssocTypes) {
709747
// For each primary associated type A of P, compute the reduced type
710748
// of T.[P]A.
711-
auto *memberType = DependentMemberType::get(type, assocType);
712-
auto reducedType = getReducedType(memberType);
749+
auto memberType = getReducedType(DependentMemberType::get(type, assocType));
713750

714751
// If the reduced type is at a lower depth than the root generic
715752
// parameter of T, then it's constrained.
716-
if (!reducedType.findIf([&](Type t) {
717-
if (auto *paramTy = t->getAs<GenericTypeParamType>())
718-
return (paramTy->getDepth() == rootDepth);
719-
return false;
720-
})) {
721-
argTypes.push_back(reducedType);
753+
if (accept(memberType)) {
754+
argTypes.push_back(getSugaredType(memberType));
722755
}
723756
}
724757

@@ -740,15 +773,18 @@ Type GenericSignatureImpl::getUpperBound(Type type) const {
740773
types.push_back(baseType);
741774
}
742775

743-
auto constraint = ProtocolCompositionType::get(getASTContext(), types,
744-
hasExplicitAnyObject);
745-
746-
if (!constraint->isConstraintType()) {
747-
assert(constraint->getClassOrBoundGenericClass());
748-
return constraint;
749-
}
776+
return ProtocolCompositionType::get(ctx, types, inverses,
777+
hasExplicitAnyObject);
778+
}
750779

751-
return ExistentialType::get(constraint);
780+
Type GenericSignatureImpl::getExistentialType(Type paramTy) const {
781+
auto upperBound = getUpperBound(paramTy,
782+
/*forExistentialSelf=*/true,
783+
/*includeParameterizedProtocols=*/true);
784+
if (upperBound->isConstraintType())
785+
return ExistentialType::get(upperBound);
786+
assert(upperBound->getClassOrBoundGenericClass());
787+
return upperBound;
752788
}
753789

754790
void GenericSignature::Profile(llvm::FoldingSetNodeID &id) const {

lib/AST/Type.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3727,9 +3727,8 @@ Type ArchetypeType::getExistentialType() const {
37273727
auto interfaceType = getInterfaceType();
37283728
auto genericSig = genericEnv->getGenericSignature();
37293729

3730-
auto upperBound = genericSig->getUpperBound(interfaceType);
3731-
3732-
return genericEnv->mapTypeIntoContext(upperBound);
3730+
auto existentialType = genericSig->getExistentialType(interfaceType);
3731+
return genericEnv->mapTypeIntoContext(existentialType);
37333732
}
37343733

37353734
bool ArchetypeType::requiresClass() const {

lib/Sema/ConstraintSystem.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2343,7 +2343,7 @@ static Type typeEraseExistentialSelfReferences(Type refTy, Type baseTy,
23432343
if (paramTy->is<GenericTypeParamType>()) {
23442344
erasedTy = baseTy;
23452345
} else {
2346-
erasedTy = existentialSig->getUpperBound(paramTy);
2346+
erasedTy = existentialSig->getExistentialType(paramTy);
23472347
}
23482348

23492349
if (metatypeDepth) {

0 commit comments

Comments
 (0)