Skip to content

Commit 4eb2968

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 4eb2968

File tree

9 files changed

+57
-177
lines changed

9 files changed

+57
-177
lines changed

include/swift/AST/GenericSignature.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,8 @@ class alignas(1 << TypeAlignInBits) GenericSignatureImpl final
360360
/// Determine whether the given dependent type is required to be a class.
361361
bool requiresClass(Type type) const;
362362

363+
Type getUpperBound(Type type, bool wantNonDependentUpperBound = true) const;
364+
363365
/// Determine the superclass bound on the given dependent type.
364366
Type getSuperclassBound(Type type) const;
365367

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: 25 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -626,149 +626,42 @@ unsigned GenericSignatureImpl::getGenericParamOrdinal(
626626
}
627627

628628
Type GenericSignatureImpl::getNonDependentUpperBounds(Type type) const {
629+
return getUpperBound(type);
630+
}
631+
632+
Type GenericSignatureImpl::getDependentUpperBounds(Type type) const {
633+
return getUpperBound(type, /*wantNonDependentBound=*/false);
634+
}
635+
636+
Type GenericSignatureImpl::getUpperBound(Type type,
637+
bool wantNonDependentBound) const {
629638
assert(type->isTypeParameter());
630639

631640
bool hasExplicitAnyObject = requiresClass(type);
632641

633642
llvm::SmallVector<Type, 2> types;
634643

635-
// Class Inheritence
636644
// If the class contains a type parameter that cannot be reduced,
637645
// try looking for a non-dependent superclass.
638646
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>();
643-
644-
SmallVector<Type, 2> argTypes;
645-
646-
for (Type argTy : boundgeneric->getGenericArgs()) {
647-
argTypes.push_back(getReducedType(argTy));
648-
}
649-
650-
boundgeneric = BoundGenericClassType::get(
651-
boundgeneric->getDecl(), boundgeneric->getParent(), argTypes);
652-
if (!boundgeneric->hasDependentMember() &&
653-
!boundgeneric->hasTypeParameter()) {
654-
superclass = boundgeneric;
655-
break;
647+
if (wantNonDependentBound) {
648+
while (superclass && superclass->hasTypeParameter()) {
649+
superclass = getReducedType(superclass);
650+
if (!superclass->hasTypeParameter())
651+
break;
652+
superclass = superclass->getSuperclass();
656653
}
657-
superclass = superclass->getSuperclass();
658654
}
655+
659656
if (superclass) {
660657
types.push_back(superclass);
661658
hasExplicitAnyObject = false;
662659
}
663660
}
664-
665-
// Protocol Inheritence
666-
// If there is a reduced type, erase to it.
667-
// Otherwise keep going until we hit a type that has unresolved components.
668-
for (auto *proto : getRequiredProtocols(type)) {
669-
if (proto->requiresClass())
670-
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-
}
770661

771-
for (auto proto : getRequiredProtocols(type)) {
662+
// If the protocol contains reduced type, erase to it.
663+
// Keep going until we hit a type that has no unresolved components.
664+
for (auto *proto : getRequiredProtocols(type)) {
772665
if (proto->requiresClass())
773666
hasExplicitAnyObject = false;
774667

@@ -813,7 +706,6 @@ Type GenericSignatureImpl::getDependentUpperBounds(Type type) const {
813706
if (!hasInnerGenericParam)
814707
argTypes.push_back(reducedType);
815708
}
816-
817709
// We should have either constrained all primary associated types,
818710
// or none of them.
819711
if (!argTypes.empty()) {
@@ -824,16 +716,17 @@ Type GenericSignatureImpl::getDependentUpperBounds(Type type) const {
824716
abort();
825717
}
826718

827-
types.push_back(ParameterizedProtocolType::get(ctx, baseType, argTypes));
719+
types.push_back(ParameterizedProtocolType::get(getASTContext(),
720+
baseType, argTypes));
828721
continue;
829722
}
830723
}
831-
832-
types.push_back(baseType);
724+
wantNonDependentBound ? types.push_back(proto->getDeclaredInterfaceType())
725+
: types.push_back(baseType);
833726
}
834727

835-
auto constraint = ProtocolCompositionType::get(
836-
ctx, types, hasExplicitAnyObject);
728+
auto constraint = ProtocolCompositionType::get(getASTContext(), types,
729+
hasExplicitAnyObject);
837730

838731
if (!constraint->isConstraintType()) {
839732
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)