Skip to content

Commit b7ea961

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 b7ea961

File tree

7 files changed

+53
-164
lines changed

7 files changed

+53
-164
lines changed

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 & 122 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,35 +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+
// check if the current class has generic arguments
648+
for (Type argTy : boundgeneric->getGenericArgs()) {
649+
argTypes.push_back(getReducedType(argTy));
650+
}
651+
652+
boundgeneric = BoundGenericClassType::get(
653+
boundgeneric->getDecl(), boundgeneric->getParent(), argTypes);
654+
if (!boundgeneric->hasDependentMember() &&
655+
!boundgeneric->hasTypeParameter()) {
656+
superclass = boundgeneric;
657+
break;
658+
}
659+
superclass = superclass->getSuperclass();
660+
// TO-DO: We should mark as 'requires coercion' if we have to erase to a non-dependent super type
656661
}
657-
superclass = superclass->getSuperclass();
658662
}
663+
659664
if (superclass) {
660665
types.push_back(superclass);
661666
hasExplicitAnyObject = false;
662667
}
663668
}
664-
669+
665670
// Protocol Inheritence
666671
// If there is a reduced type, erase to it.
667-
// Otherwise keep going until we hit a type that has unresolved components.
672+
// Otherwise keep going until we hit a type that has
673+
// no unresolved components.
668674
for (auto *proto : getRequiredProtocols(type)) {
669675
if (proto->requiresClass())
670676
hasExplicitAnyObject = false;
@@ -720,12 +726,12 @@ Type GenericSignatureImpl::getNonDependentUpperBounds(Type type) const {
720726
abort();
721727
}
722728

723-
types.push_back(ParameterizedProtocolType::get(getASTContext(), baseType, argTypes));
729+
types.push_back(ParameterizedProtocolType::get(getASTContext(),
730+
baseType, argTypes));
724731
continue;
725732
}
726733
}
727-
728-
types.push_back(baseType);
734+
types.push_back(proto->getDeclaredInterfaceType());
729735
}
730736

731737
auto constraint = ProtocolCompositionType::get(
@@ -740,109 +746,6 @@ Type GenericSignatureImpl::getNonDependentUpperBounds(Type type) const {
740746
return ExistentialType::get(constraint);
741747
}
742748

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;
774-
775-
auto *baseType = proto->getDeclaredInterfaceType()->castTo<ProtocolType>();
776-
777-
auto primaryAssocTypes = proto->getPrimaryAssociatedTypes();
778-
if (!primaryAssocTypes.empty()) {
779-
SmallVector<Type, 2> argTypes;
780-
781-
// Attempt to recover same-type requirements on primary associated types.
782-
for (auto *assocType : primaryAssocTypes) {
783-
// For each primary associated type A of P, compute the reduced type
784-
// of T.[P]A.
785-
auto *memberType = DependentMemberType::get(type, assocType);
786-
auto reducedType = getReducedType(memberType);
787-
788-
// If the reduced type is at a lower depth than the root generic
789-
// parameter of T, then it's constrained.
790-
bool hasOuterGenericParam = false;
791-
bool hasInnerGenericParam = false;
792-
reducedType.visit([&](Type t) {
793-
if (auto *paramTy = t->getAs<GenericTypeParamType>()) {
794-
unsigned rootDepth = type->getRootGenericParam()->getDepth();
795-
if (paramTy->getDepth() == rootDepth)
796-
hasInnerGenericParam = true;
797-
else {
798-
assert(paramTy->getDepth() < rootDepth);
799-
hasOuterGenericParam = true;
800-
}
801-
}
802-
});
803-
804-
if (hasInnerGenericParam && hasOuterGenericParam) {
805-
llvm::errs() << "Weird same-type requirements?\n";
806-
llvm::errs() << "Interface type: " << type << "\n";
807-
llvm::errs() << "Member type: " << memberType << "\n";
808-
llvm::errs() << "Reduced member type: " << reducedType << "\n";
809-
llvm::errs() << GenericSignature(this) << "\n";
810-
abort();
811-
}
812-
813-
if (!hasInnerGenericParam)
814-
argTypes.push_back(reducedType);
815-
}
816-
817-
// We should have either constrained all primary associated types,
818-
// or none of them.
819-
if (!argTypes.empty()) {
820-
if (argTypes.size() != primaryAssocTypes.size()) {
821-
llvm::errs() << "Not all primary associated types constrained?\n";
822-
llvm::errs() << "Interface type: " << type << "\n";
823-
llvm::errs() << GenericSignature(this) << "\n";
824-
abort();
825-
}
826-
827-
types.push_back(ParameterizedProtocolType::get(ctx, baseType, argTypes));
828-
continue;
829-
}
830-
}
831-
832-
types.push_back(baseType);
833-
}
834-
835-
auto constraint = ProtocolCompositionType::get(
836-
ctx, types, hasExplicitAnyObject);
837-
838-
if (!constraint->isConstraintType()) {
839-
assert(constraint->getClassOrBoundGenericClass());
840-
return constraint;
841-
}
842-
843-
return ExistentialType::get(constraint);
844-
}
845-
846749
void GenericSignature::Profile(llvm::FoldingSetNodeID &id) const {
847750
return GenericSignature::Profile(id, getPointer()->getGenericParams(),
848751
getPointer()->getRequirements());

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/CSFix.cpp

Lines changed: 1 addition & 9 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.
@@ -2516,11 +2514,6 @@ bool AddExplicitExistentialCoercion::isRequired(
25162514
return Action::Stop;
25172515
}
25182516

2519-
if (erasedMemberTy->hasTypeVariable()) {
2520-
RequiresCoercion = true;
2521-
return Action::Stop;
2522-
}
2523-
25242517
return Action::SkipChildren;
25252518
}
25262519

@@ -2568,7 +2561,6 @@ bool AddExplicitExistentialCoercion::isRequired(
25682561
case RequirementKind::Layout: {
25692562
if (isAnchoredOn(req.getFirstType(), member->getAssocType()))
25702563
return true;
2571-
25722564
break;
25732565
}
25742566

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>) {

test/decl/protocol/existential_member_accesses_self_assoctype.swift

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,6 @@ do {
637637
let exist: any InvalidTypeParameters
638638

639639
exist.method1() // expected-error {{instance method 'method1()' requires that 'Self.A' conform to 'InvalidTypeParameters'}}
640-
// expected-error@-1 {{inferred result type '(any InvalidTypeParameters).A.A' requires explicit coercion due to loss of generic requirements}}
641640
exist.method2(false) // expected-error {{instance method 'method2' requires that 'Self.A' conform to 'InvalidTypeParameters'}}
642641
exist.method3(false, false) // expected-error {{instance method 'method3' requires that 'Self.A' conform to 'InvalidTypeParameters'}}
643642
// expected-error@-1 {{member 'method3' cannot be used on value of type 'any InvalidTypeParameters'; consider using a generic constraint instead}}
@@ -795,14 +794,14 @@ do {
795794

796795
let _: (
797796
Struct<Bool>, (any ConcreteAssocTypes).Type, () -> Bool
798-
) -> any Class<Struct<Bool>.Inner> & ConcreteAssocTypes = arg.method4 // expected-error {{inferred result type 'any Class<Struct<(any ConcreteAssocTypes).A7>.Inner> & ConcreteAssocTypes' requires explicit coercion due to loss of generic requirements}}
797+
) -> any Class<Struct<Bool>.Inner> & ConcreteAssocTypes = arg.method4
799798

800799
let _: (
801800
Struct<Bool>, (any ConcreteAssocTypes).Type, () -> Bool
802801
) -> any Class<Struct<Bool>.Inner> & ConcreteAssocTypes = arg.property4
803802

804803
let _: any Class<Struct<Bool>.Inner> & ConcreteAssocTypes =
805-
arg[ // expected-error {{inferred result type 'any Class<Struct<(any ConcreteAssocTypes).A7>.Inner> & ConcreteAssocTypes' requires explicit coercion due to loss of generic requirements}}
804+
arg[
806805
subscript4: Struct<Bool>(), (any ConcreteAssocTypes).self, { true }
807806
]
808807
}
@@ -929,16 +928,14 @@ do {
929928
let _ = exist.method2()
930929
let _ = exist.method3()
931930
let _ = exist.method4()
932-
let _ = exist.method5() // expected-error {{inferred result type 'Class2Base' requires explicit coercion due to loss of generic requirements}}{{24-24=as Class2Base}}
931+
let _ = exist.method5()
933932
let _ = exist.method6()
934-
let _ = exist.method7() // expected-error {{inferred result type 'any Class2Base & CovariantAssocTypeErasure' requires explicit coercion due to loss of generic requirements}}{{24-24=as any Class2Base & CovariantAssocTypeErasure}}
933+
let _ = exist.method7()
935934
let _ = exist.method8()
936935
let _ = exist.method9()
937936
let _ = exist.method10()
938937
let _ = exist.method11()
939-
let _ = exist.method12() // expected-error {{inferred result type 'Dictionary<String, Class2Base>' requires explicit coercion due to loss of generic requirements}}{{25-25=as Dictionary<String, Class2Base>}}
940-
941-
938+
let _ = exist.method12()
942939
}
943940
do {
944941
let exist: any CovariantAssocTypeErasureDerived

0 commit comments

Comments
 (0)