Skip to content

Commit f78b63c

Browse files
committed
[Generic Signature] Unify generic upper bound functions
Opened existentials should be erased to the dependent upper bound if the dependent member can be reduced to a concrete type. This allows the generic signature to support parameterized protocol types and bound generic class types by producing a more specific constraint instead of just a plain protocol or class.
1 parent 9a02283 commit f78b63c

File tree

9 files changed

+55
-179
lines changed

9 files changed

+55
-179
lines changed

include/swift/AST/GenericSignature.h

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -453,20 +453,12 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final
453453
/// generic parameter types by their sugared form.
454454
Type getSugaredType(Type type) const;
455455

456-
/// Given a type parameter, compute the most specific supertype (upper bound)
457-
/// that is not dependent on other type parameters.
456+
/// Given a type parameter, compute the most specific supertype (upper bound).
458457
///
459458
/// \note If the upper bound is a protocol or protocol composition,
460459
/// will return an instance of \c ExistentialType.
461460
Type getNonDependentUpperBounds(Type type) const;
462461

463-
/// Given a type parameter, compute the most specific supertype (upper bound)
464-
/// that is possibly dependent on other type parameters.
465-
///
466-
/// \note If the upper bound is a protocol or protocol composition,
467-
/// will return an instance of \c ExistentialType.
468-
Type getDependentUpperBounds(Type type) const;
469-
470462
static void Profile(llvm::FoldingSetNodeID &ID,
471463
TypeArrayView<GenericTypeParamType> genericParams,
472464
ArrayRef<Requirement> requirements);

include/swift/Sema/ConstraintSystem.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5532,12 +5532,7 @@ Expr *getArgumentLabelTargetExpr(Expr *fn);
55325532
/// variable and anything that depends on it to their non-dependent bounds.
55335533
Type typeEraseOpenedExistentialReference(Type type, Type existentialBaseType,
55345534
TypeVariableType *openedTypeVar,
5535-
TypePosition outermostPosition,
5536-
bool wantNonDependentBound = true);
5537-
5538-
Type transformFn(Type type, Type existentialBaseType,
5539-
TypePosition initialPos);
5540-
5535+
TypePosition outermostPosition);
55415536

55425537
/// Returns true if a reference to a member on a given base type will apply
55435538
/// its curried self parameter, assuming it has one.

lib/AST/GenericSignature.cpp

Lines changed: 24 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,8 @@ unsigned GenericSignatureImpl::getGenericParamOrdinal(
628628
Type GenericSignatureImpl::getNonDependentUpperBounds(Type type) const {
629629
assert(type->isTypeParameter());
630630

631+
auto &ctx = type->getASTContext();
632+
631633
bool hasExplicitAnyObject = requiresClass(type);
632634

633635
llvm::SmallVector<Type, 2> types;
@@ -636,141 +638,39 @@ Type GenericSignatureImpl::getNonDependentUpperBounds(Type type) const {
636638
// If the class contains a type parameter that cannot be reduced,
637639
// try looking for a non-dependent superclass.
638640
if (Type superclass = getSuperclassBound(type)) {
639-
while (superclass &&
640-
superclass->hasTypeParameter()) { // check if the current protocol
641-
// has an associated type]
642-
auto *boundgeneric = superclass->castTo<BoundGenericClassType>();
641+
if (auto *boundgeneric = dyn_cast<BoundGenericClassType>(superclass)) {
643642

644643
SmallVector<Type, 2> argTypes;
645644

646-
for (Type argTy : boundgeneric->getGenericArgs()) {
647-
argTypes.push_back(getReducedType(argTy));
648-
}
645+
while (superclass && superclass->hasTypeParameter()) {
649646

650-
boundgeneric = BoundGenericClassType::get(
651-
boundgeneric->getDecl(), boundgeneric->getParent(), argTypes);
652-
if (!boundgeneric->hasDependentMember() &&
653-
!boundgeneric->hasTypeParameter()) {
654-
superclass = boundgeneric;
655-
break;
647+
argTypes.push_back(getReducedType(superclass));
648+
649+
boundgeneric = BoundGenericClassType::get(
650+
boundgeneric->getDecl(), boundgeneric->getParent(), argTypes);
651+
if (!boundgeneric->hasTypeParameter()) {
652+
superclass = boundgeneric;
653+
break;
654+
}
655+
superclass = superclass->getSuperclass();
656+
// TO-DO: We should mark as 'requires coercion' if we have to erase to a
657+
// non-dependent super type
656658
}
657-
superclass = superclass->getSuperclass();
658659
}
660+
659661
if (superclass) {
660662
types.push_back(superclass);
661663
hasExplicitAnyObject = false;
662664
}
663665
}
664-
666+
665667
// Protocol Inheritence
666668
// If there is a reduced type, erase to it.
667-
// Otherwise keep going until we hit a type that has unresolved components.
669+
// Otherwise keep going until we hit a type that has
670+
// no unresolved components.
668671
for (auto *proto : getRequiredProtocols(type)) {
669672
if (proto->requiresClass())
670673
hasExplicitAnyObject = false;
671-
672-
auto *baseType = proto->getDeclaredInterfaceType()->castTo<ProtocolType>();
673-
674-
auto primaryAssocTypes = proto->getPrimaryAssociatedTypes();
675-
if (!primaryAssocTypes.empty()) {
676-
SmallVector<Type, 2> argTypes;
677-
678-
// Attempt to recover same-type requirements on primary associated types.
679-
for (auto *assocType : primaryAssocTypes) {
680-
// For each primary associated type A of P, compute the reduced type
681-
// of T.[P]A.
682-
auto *memberType = DependentMemberType::get(type, assocType);
683-
auto reducedType = getReducedType(memberType);
684-
685-
// If the reduced type is at a lower depth than the root generic
686-
// parameter of T, then it's constrained.
687-
bool hasOuterGenericParam = false;
688-
bool hasInnerGenericParam = false;
689-
reducedType.visit([&](Type t) {
690-
if (auto *paramTy = t->getAs<GenericTypeParamType>()) {
691-
unsigned rootDepth = type->getRootGenericParam()->getDepth();
692-
if (paramTy->getDepth() == rootDepth)
693-
hasInnerGenericParam = true;
694-
else {
695-
assert(paramTy->getDepth() < rootDepth);
696-
hasOuterGenericParam = true;
697-
}
698-
}
699-
});
700-
701-
if (hasInnerGenericParam && hasOuterGenericParam) {
702-
llvm::errs() << "Weird same-type requirements?\n";
703-
llvm::errs() << "Interface type: " << type << "\n";
704-
llvm::errs() << "Member type: " << memberType << "\n";
705-
llvm::errs() << "Reduced member type: " << reducedType << "\n";
706-
llvm::errs() << GenericSignature(this) << "\n";
707-
abort();
708-
}
709-
710-
if (!hasInnerGenericParam)
711-
argTypes.push_back(reducedType);
712-
}
713-
// We should have either constrained all primary associated types,
714-
// or none of them.
715-
if (!argTypes.empty()) {
716-
if (argTypes.size() != primaryAssocTypes.size()) {
717-
llvm::errs() << "Not all primary associated types constrained?\n";
718-
llvm::errs() << "Interface type: " << type << "\n";
719-
llvm::errs() << GenericSignature(this) << "\n";
720-
abort();
721-
}
722-
723-
types.push_back(ParameterizedProtocolType::get(getASTContext(), baseType, argTypes));
724-
continue;
725-
}
726-
}
727-
728-
types.push_back(baseType);
729-
}
730-
731-
auto constraint = ProtocolCompositionType::get(
732-
getASTContext(), types,
733-
hasExplicitAnyObject);
734-
735-
if (!constraint->isConstraintType()) {
736-
assert(constraint->getClassOrBoundGenericClass());
737-
return constraint;
738-
}
739-
740-
return ExistentialType::get(constraint);
741-
}
742-
743-
Type GenericSignatureImpl::getDependentUpperBounds(Type type) const {
744-
assert(type->isTypeParameter());
745-
746-
llvm::SmallVector<Type, 2> types;
747-
748-
auto &ctx = type->getASTContext();
749-
750-
bool hasExplicitAnyObject = requiresClass(type);
751-
752-
// FIXME: If the superclass bound is implied by one of our protocols, we
753-
// shouldn't add it to the constraint type.
754-
if (Type superclass = getSuperclassBound(type)) {
755-
hasExplicitAnyObject = false;
756-
757-
if (auto *boundgeneric = superclass->getAs<BoundGenericClassType>()) {
758-
SmallVector<Type, 2> argTypes;
759-
760-
for (Type argTy : boundgeneric->getGenericArgs()) {
761-
argTypes.push_back(getReducedType(argTy));
762-
}
763-
boundgeneric = BoundGenericClassType::get(
764-
boundgeneric->getDecl(), boundgeneric->getParent(), argTypes);
765-
types.push_back(boundgeneric);
766-
} else {
767-
types.push_back(superclass);
768-
}
769-
}
770-
771-
for (auto proto : getRequiredProtocols(type)) {
772-
if (proto->requiresClass())
773-
hasExplicitAnyObject = false;
774674

775675
auto *baseType = proto->getDeclaredInterfaceType()->castTo<ProtocolType>();
776676

@@ -813,7 +713,6 @@ Type GenericSignatureImpl::getDependentUpperBounds(Type type) const {
813713
if (!hasInnerGenericParam)
814714
argTypes.push_back(reducedType);
815715
}
816-
817716
// We should have either constrained all primary associated types,
818717
// or none of them.
819718
if (!argTypes.empty()) {
@@ -824,16 +723,16 @@ Type GenericSignatureImpl::getDependentUpperBounds(Type type) const {
824723
abort();
825724
}
826725

827-
types.push_back(ParameterizedProtocolType::get(ctx, baseType, argTypes));
726+
types.push_back(ParameterizedProtocolType::get(getASTContext(),
727+
baseType, argTypes));
828728
continue;
829729
}
830730
}
831-
832-
types.push_back(baseType);
731+
types.push_back(proto->getDeclaredInterfaceType());
833732
}
834733

835-
auto constraint = ProtocolCompositionType::get(
836-
ctx, types, hasExplicitAnyObject);
734+
auto constraint = ProtocolCompositionType::get(getASTContext(), types,
735+
hasExplicitAnyObject);
837736

838737
if (!constraint->isConstraintType()) {
839738
assert(constraint->getClassOrBoundGenericClass());

lib/AST/Type.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3572,7 +3572,7 @@ Type ArchetypeType::getExistentialType() const {
35723572
auto interfaceType = getInterfaceType();
35733573
auto genericSig = genericEnv->getGenericSignature();
35743574

3575-
auto upperBound = genericSig->getDependentUpperBounds(interfaceType);
3575+
auto upperBound = genericSig->getNonDependentUpperBounds(interfaceType);
35763576

35773577
return genericEnv->mapTypeIntoContext(upperBound);
35783578
}

lib/Sema/CSDiagnostics.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8761,7 +8761,7 @@ bool MissingExplicitExistentialCoercion::fixItRequiresParens() const {
87618761
void MissingExplicitExistentialCoercion::fixIt(
87628762
InFlightDiagnostic &diagnostic) const {
87638763

8764-
if (ErasedResultType->hasDependentMember())
8764+
if (ErasedResultType->hasTypeParameter())
87658765
return;
87668766

87678767
bool requiresParens = fixItRequiresParens();

lib/Sema/CSFix.cpp

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2496,10 +2496,8 @@ bool AddExplicitExistentialCoercion::isRequired(
24962496
if (!existentialType)
24972497
return Action::SkipChildren;
24982498

2499-
// let's grab the dependent upper bound here instead of nondependent
25002499
auto erasedMemberTy = typeEraseOpenedExistentialReference(
2501-
Type(member), *existentialType, typeVar, TypePosition::Covariant,
2502-
false);
2500+
Type(member), *existentialType, typeVar, TypePosition::Covariant);
25032501

25042502
// If result is an existential type and the base has `where` clauses
25052503
// associated with its associated types, the call needs a coercion.
@@ -2510,13 +2508,7 @@ bool AddExplicitExistentialCoercion::isRequired(
25102508
}
25112509

25122510
if (erasedMemberTy->isExistentialType() &&
2513-
(erasedMemberTy->hasDependentMember() ||
2514-
erasedMemberTy->hasTypeParameter())) {
2515-
RequiresCoercion = true;
2516-
return Action::Stop;
2517-
}
2518-
2519-
if (erasedMemberTy->hasTypeVariable()) {
2511+
erasedMemberTy->hasTypeParameter()) {
25202512
RequiresCoercion = true;
25212513
return Action::Stop;
25222514
}
@@ -2568,7 +2560,6 @@ bool AddExplicitExistentialCoercion::isRequired(
25682560
case RequirementKind::Layout: {
25692561
if (isAnchoredOn(req.getFirstType(), member->getAssocType()))
25702562
return true;
2571-
25722563
break;
25732564
}
25742565

lib/Sema/ConstraintSystem.cpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1997,10 +1997,8 @@ static bool isMainDispatchQueueMember(ConstraintLocator *locator) {
19971997
///
19981998
/// \note If a 'Self'-rooted type parameter is bound to a concrete type, this
19991999
/// routine will recurse into the concrete type.
2000-
static Type
2001-
typeEraseExistentialSelfReferences(Type refTy, Type baseTy,
2002-
TypePosition outermostPosition,
2003-
bool wantNonDependentBound = true) {
2000+
static Type typeEraseExistentialSelfReferences(Type refTy, Type baseTy,
2001+
TypePosition outermostPosition) {
20042002
assert(baseTy->isExistentialType());
20052003
if (!refTy->hasTypeParameter()) {
20062004
return refTy;
@@ -2103,9 +2101,7 @@ typeEraseExistentialSelfReferences(Type refTy, Type baseTy,
21032101
if (t->is<GenericTypeParamType>()) {
21042102
erasedTy = baseTy;
21052103
} else {
2106-
erasedTy = wantNonDependentBound
2107-
? existentialSig->getNonDependentUpperBounds(t)
2108-
: existentialSig->getDependentUpperBounds(t);
2104+
erasedTy = existentialSig->getNonDependentUpperBounds(t);
21092105
}
21102106

21112107
if (metatypeDepth) {
@@ -2121,7 +2117,7 @@ typeEraseExistentialSelfReferences(Type refTy, Type baseTy,
21212117

21222118
Type constraints::typeEraseOpenedExistentialReference(
21232119
Type type, Type existentialBaseType, TypeVariableType *openedTypeVar,
2124-
TypePosition outermostPosition, bool wantNonDependentBound) {
2120+
TypePosition outermostPosition) {
21252121
Type selfGP = GenericTypeParamType::get(false, 0, 0, type->getASTContext());
21262122

21272123
// First, temporarily reconstitute the 'Self' generic parameter.
@@ -2138,8 +2134,8 @@ Type constraints::typeEraseOpenedExistentialReference(
21382134
});
21392135

21402136
// Then, type-erase occurrences of covariant 'Self'-rooted type parameters.
2141-
type = typeEraseExistentialSelfReferences(
2142-
type, existentialBaseType, outermostPosition, wantNonDependentBound);
2137+
type = typeEraseExistentialSelfReferences(type, existentialBaseType,
2138+
outermostPosition);
21432139

21442140
// Finally, swap the 'Self'-corresponding type variable back in.
21452141
return type.transformRec([&](TypeBase *t) -> Optional<Type> {

test/Constraints/opened_existentials.swift

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ func testExplicitCoercionRequirement(v: any B, otherV: any B & D) {
256256
_ = getTuple(v) // expected-error {{inferred result type '(any B, any P)' requires explicit coercion due to loss of generic requirements}} {{18-18=as (any B, any P)}}
257257
_ = getTuple(v) as (any B, any P) // Ok
258258
// Ok because T.C.A == Double
259-
_ = getNoError(v) // expected-error {{inferred result type '(any B).C.A' requires explicit coercion due to loss of generic requirements}}
259+
_ = getNoError(v)
260260

261261
_ = getComplex(v) // expected-error {{inferred result type '([(x: (a: any P, b: Int), y: Int)], [Int : any P])' requires explicit coercion due to loss of generic requirements}} {{20-20=as ([(x: (a: any P, b: Int), y: Int)], [Int : any P])}}
262262
_ = getComplex(v) as ([(x: (a: any P, b: Int), y: Int)], [Int : any P]) // Ok
@@ -306,7 +306,6 @@ func testExplicitCoercionRequirement(v: any B, otherV: any B & D) {
306306
getP((v.getC() as any P)) // Ok - parens avoid opening suppression
307307
}
308308

309-
// Generic Class Types
310309
class C1 {}
311310
class C2<T>: C1 {}
312311

@@ -318,8 +317,15 @@ protocol P1 {
318317
func returnAssocTypeB() -> B
319318
}
320319

321-
func testAssocReturn(p: any P1) { // should return C1
322-
let _ = p.returnAssocTypeB() // expected-error {{inferred result type 'C1' requires explicit coercion due to loss of generic requirements}} {{29-29=as C1}}
320+
func testAssocReturn(p: any P1) {
321+
let _ = p.returnAssocTypeB() // returns C1
322+
}
323+
324+
protocol Q2 : P1 where A == Int {}
325+
326+
do {
327+
let q: any Q2
328+
let _ = q.returnAssocTypeB() // returns C1
323329
}
324330

325331
// Test Primary Associated Types
@@ -330,8 +336,8 @@ protocol P2<A> {
330336
func returnAssocTypeB() -> B
331337
}
332338

333-
func testAssocReturn(p: any P2<Int>) { // should return C2<A>
334-
let _ = p.returnAssocTypeB()
339+
func testAssocReturn(p: any P2<Int>) {
340+
let _ = p.returnAssocTypeB() // returns C2<A>
335341
}
336342

337343
func testAssocReturn(p: any P2<any P2<String>>) {
@@ -346,9 +352,9 @@ protocol P3<A> {
346352
func returnAssocTypeCollection() -> any Collection<A>
347353
}
348354

349-
// Confirm there is no way to access Primary Associated Type
355+
// Confirm there is no way to access Primary Associated Type directly
350356
func testPrimaryAssocReturn(p: any P3<Int>) {
351-
let _ = p.returnPrimaryAssocTypeA() //expected-error {{inferred result type '(any P3<Int>).A' requires explicit coercion due to loss of generic requirements}}
357+
let _ = p.returnPrimaryAssocTypeA()
352358
}
353359

354360
func testPrimaryAssocCollection(p: any P3<Float>) {

0 commit comments

Comments
 (0)