Skip to content

Commit a6dbfa9

Browse files
authored
Merge pull request #71303 from slavapestov/ncgenerics-fixes-2
Non-copyable generics fixes part 2
2 parents 6f4abe7 + 066cb8b commit a6dbfa9

21 files changed

+212
-143
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,

include/swift/AST/Types.h

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5943,24 +5943,21 @@ class ProtocolCompositionType final : public TypeBase,
59435943

59445944
public:
59455945
/// Retrieve an instance of a protocol composition type with the
5946-
/// given set of members. A "hidden member" is an implicit constraint that
5947-
/// is present for all protocol compositions.
5948-
///
5949-
/// \param Members the regular members of this composition.
5950-
/// \param Inverses the set of inverses that are a member of the composition,
5951-
/// i.e., if \c IP is in this set, then \c ~IP is a member of
5952-
/// this composition.
5953-
/// \param HasExplicitAnyObject indicates whether this composition should be
5954-
/// treated as if \c AnyObject was a member.
5946+
/// given set of members.
5947+
///
5948+
/// This presents a syntactic view of the world, where an empty composition
5949+
/// has implicit Copyable and Escapable members, unless they are supressed
5950+
/// with the Inverses field.
5951+
///
5952+
/// The list of members consists of zero or more ProtocolType,
5953+
/// ProtocolCompositionType, ParameterizedProtocolType, together with at
5954+
/// most one ClassType or BoundGenericClassType.
5955+
///
5956+
/// HasExplicitAnyObject is the 'AnyObject' member.
59555957
static Type get(const ASTContext &C, ArrayRef<Type> Members,
59565958
InvertibleProtocolSet Inverses,
59575959
bool HasExplicitAnyObject);
59585960

5959-
/// Retrieve an instance of a protocol composition type with the
5960-
/// given set of members. Assumes no inverses are present in \c Members.
5961-
static Type get(const ASTContext &C, ArrayRef<Type> Members,
5962-
bool HasExplicitAnyObject);
5963-
59645961
/// Constructs a protocol composition corresponding to the `Any` type.
59655962
static Type theAnyType(const ASTContext &C);
59665963

lib/AST/ASTContext.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3842,6 +3842,15 @@ ProtocolCompositionType::build(const ASTContext &C, ArrayRef<Type> Members,
38423842
bool HasExplicitAnyObject) {
38433843
assert(Members.size() != 1 || HasExplicitAnyObject || !Inverses.empty());
38443844

3845+
#ifndef NDEBUG
3846+
for (auto member : Members) {
3847+
if (auto *proto = member->getAs<ProtocolType>()) {
3848+
assert(!proto->getDecl()->getInvertibleProtocolKind() &&
3849+
"Should have been folded away");
3850+
}
3851+
}
3852+
#endif
3853+
38453854
// Check to see if we've already seen this protocol composition before.
38463855
void *InsertPos = nullptr;
38473856
llvm::FoldingSetNodeID ID;

lib/AST/ASTDemangler.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,10 @@ Type ASTBuilder::createProtocolCompositionType(
673673
if (superclass && superclass->getClassOrBoundGenericClass())
674674
members.push_back(superclass);
675675

676-
Type composition = ProtocolCompositionType::get(Ctx, members, isClassBound);
676+
// FIXME: move-only generics
677+
InvertibleProtocolSet inverses;
678+
Type composition = ProtocolCompositionType::get(Ctx, members, inverses,
679+
isClassBound);
677680
if (forRequirement)
678681
return composition;
679682

lib/AST/ExistentialGeneralization.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ class Generalizer : public CanTypeVisitor<Generalizer, Type> {
107107
newMembers.push_back(generalizeStructure(origMember));
108108
}
109109
return ProtocolCompositionType::get(ctx, newMembers,
110+
origType->getInverses(),
110111
origType->hasExplicitAnyObject());
111112
}
112113

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: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,7 @@ Type TypeBase::stripConcurrency(bool recurse, bool dropGlobalActor) {
11411141
if (!newMembers.empty()) {
11421142
return ProtocolCompositionType::get(
11431143
getASTContext(), newMembers,
1144+
protocolCompositionType->getInverses(),
11441145
protocolCompositionType->hasExplicitAnyObject());
11451146
}
11461147

@@ -1606,18 +1607,20 @@ static void addProtocols(Type T,
16061607
SmallVectorImpl<ProtocolDecl *> &Protocols,
16071608
ParameterizedProtocolMap &Parameterized,
16081609
Type &Superclass,
1610+
InvertibleProtocolSet &Inverses,
16091611
bool &HasExplicitAnyObject) {
16101612
if (auto Proto = T->getAs<ProtocolType>()) {
16111613
Protocols.push_back(Proto->getDecl());
16121614
return;
16131615
}
16141616

16151617
if (auto PC = T->getAs<ProtocolCompositionType>()) {
1616-
if (PC->hasExplicitAnyObject())
1617-
HasExplicitAnyObject = true;
1618-
for (auto P : PC->getMembers())
1619-
addProtocols(P, Protocols, Parameterized, Superclass,
1618+
Inverses.insertAll(PC->getInverses());
1619+
HasExplicitAnyObject |= PC->hasExplicitAnyObject();
1620+
for (auto P : PC->getMembers()) {
1621+
addProtocols(P, Protocols, Parameterized, Superclass, Inverses,
16201622
HasExplicitAnyObject);
1623+
}
16211624
return;
16221625
}
16231626

@@ -1918,6 +1921,7 @@ CanType TypeBase::computeCanonicalType() {
19181921
assert(!CanProtos.empty() && "Non-canonical empty composition?");
19191922
const ASTContext &C = CanProtos[0]->getASTContext();
19201923
Type Composition = ProtocolCompositionType::get(C, CanProtos,
1924+
PCT->getInverses(),
19211925
PCT->hasExplicitAnyObject());
19221926
Result = Composition.getPointer();
19231927
break;
@@ -3727,9 +3731,8 @@ Type ArchetypeType::getExistentialType() const {
37273731
auto interfaceType = getInterfaceType();
37283732
auto genericSig = genericEnv->getGenericSignature();
37293733

3730-
auto upperBound = genericSig->getUpperBound(interfaceType);
3731-
3732-
return genericEnv->mapTypeIntoContext(upperBound);
3734+
auto existentialType = genericSig->getExistentialType(interfaceType);
3735+
return genericEnv->mapTypeIntoContext(existentialType);
37333736
}
37343737

37353738
bool ArchetypeType::requiresClass() const {
@@ -4010,27 +4013,22 @@ bool ProtocolCompositionType::requiresClass() {
40104013

40114014
/// Constructs a protocol composition corresponding to the `Any` type.
40124015
Type ProtocolCompositionType::theAnyType(const ASTContext &C) {
4013-
return ProtocolCompositionType::get(C, {}, /*HasExplicitAnyObject=*/false);
4016+
return ProtocolCompositionType::get(C, {}, /*Inverses=*/{},
4017+
/*HasExplicitAnyObject=*/false);
40144018
}
40154019

40164020
/// Constructs a protocol composition containing the `AnyObject` constraint.
40174021
Type ProtocolCompositionType::theAnyObjectType(const ASTContext &C) {
4018-
return ProtocolCompositionType::get(C, {}, /*HasExplicitAnyObject=*/true);
4022+
return ProtocolCompositionType::get(C, {}, /*Inverses=*/{},
4023+
/*HasExplicitAnyObject=*/true);
40194024
}
40204025

40214026
Type ProtocolCompositionType::getInverseOf(const ASTContext &C,
40224027
InvertibleProtocolKind IP) {
4023-
return ProtocolCompositionType::get(C, {}, {IP},
4028+
return ProtocolCompositionType::get(C, {}, /*Inverses=*/{IP},
40244029
/*HasExplicitAnyObject=*/false);
40254030
}
40264031

4027-
Type ProtocolCompositionType::get(const ASTContext &C, ArrayRef<Type> Members,
4028-
bool HasExplicitAnyObject) {
4029-
return ProtocolCompositionType::get(C, Members,
4030-
/*Inverses=*/{},
4031-
HasExplicitAnyObject);
4032-
}
4033-
40344032
Type ProtocolCompositionType::get(const ASTContext &C,
40354033
ArrayRef<Type> Members,
40364034
InvertibleProtocolSet Inverses,
@@ -4056,29 +4054,29 @@ Type ProtocolCompositionType::get(const ASTContext &C,
40564054
if (!t->isCanonical())
40574055
return build(C, Members, Inverses, HasExplicitAnyObject);
40584056
}
4059-
4057+
40604058
Type Superclass;
40614059
SmallVector<ProtocolDecl *, 4> Protocols;
40624060
ParameterizedProtocolMap Parameterized;
40634061
for (Type t : Members) {
4064-
addProtocols(t, Protocols, Parameterized, Superclass, HasExplicitAnyObject);
4062+
addProtocols(t, Protocols, Parameterized, Superclass,
4063+
Inverses, HasExplicitAnyObject);
40654064
}
40664065

4066+
// Form the set of canonical component types.
4067+
SmallVector<Type, 4> CanTypes;
4068+
40674069
// The presence of a superclass constraint makes AnyObject redundant.
4068-
if (Superclass)
4070+
if (Superclass) {
40694071
HasExplicitAnyObject = false;
4072+
CanTypes.push_back(Superclass->getCanonicalType());
4073+
}
40704074

40714075
// If there are any parameterized protocols, the canonicalization
40724076
// algorithm gets more complex.
4073-
4074-
// Form the set of canonical component types.
4075-
SmallVector<Type, 4> CanTypes;
4076-
if (Superclass)
4077-
CanTypes.push_back(Superclass->getCanonicalType());
4078-
40794077
canonicalizeProtocols(Protocols, &Parameterized);
40804078

4081-
for (auto proto: Protocols) {
4079+
for (auto proto : Protocols) {
40824080
// If we have a parameterized type for this protocol, use the
40834081
// canonical type of that. Sema should prevent us from building
40844082
// compositions with the same protocol and conflicting constraints.
@@ -5347,6 +5345,7 @@ case TypeKind::Id:
53475345

53485346
return ProtocolCompositionType::get(Ptr->getASTContext(),
53495347
substMembers,
5348+
pc->getInverses(),
53505349
pc->hasExplicitAnyObject());
53515350
}
53525351

lib/AST/TypeJoinMeet.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,8 @@ CanType TypeJoin::computeProtocolCompositionJoin(ArrayRef<Type> firstMembers,
404404
return TheAnyType;
405405

406406
auto &ctx = result[0]->getASTContext();
407-
return ProtocolCompositionType::get(ctx, result, false)->getCanonicalType();
407+
return ProtocolCompositionType::get(ctx, result, /*inverses=*/{},
408+
false)->getCanonicalType();
408409
}
409410

410411
CanType TypeJoin::visitProtocolCompositionType(CanType second) {
@@ -431,8 +432,12 @@ CanType TypeJoin::visitProtocolCompositionType(CanType second) {
431432
protocolType.push_back(First);
432433
firstMembers = protocolType;
433434
} else {
435+
assert(cast<ProtocolCompositionType>(First)->getInverses().empty() &&
436+
"FIXME: move-only generics");
434437
firstMembers = cast<ProtocolCompositionType>(First)->getMembers();
435438
}
439+
assert(cast<ProtocolCompositionType>(second)->getInverses().empty() &&
440+
"FIXME: move-only generics");
436441
auto secondMembers = cast<ProtocolCompositionType>(second)->getMembers();
437442

438443
return computeProtocolCompositionJoin(firstMembers, secondMembers);

lib/ClangImporter/ImportType.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,7 @@ namespace {
12431243

12441244
importedType = ExistentialType::get(
12451245
ProtocolCompositionType::get(Impl.SwiftContext, members,
1246+
/*Inverses=*/{},
12461247
/*HasExplicitAnyObject=*/false));
12471248
}
12481249

0 commit comments

Comments
 (0)