Skip to content

Commit 5c818b3

Browse files
committed
[Constraint System] Fix covariant erasure for constrained existentials
Constrained existentials should be type erased to an upper bound that is dependent on other type parameters.
1 parent 61ea49c commit 5c818b3

File tree

9 files changed

+223
-22
lines changed

9 files changed

+223
-22
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5579,7 +5579,12 @@ Expr *getArgumentLabelTargetExpr(Expr *fn);
55795579
/// variable and anything that depends on it to their non-dependent bounds.
55805580
Type typeEraseOpenedExistentialReference(Type type, Type existentialBaseType,
55815581
TypeVariableType *openedTypeVar,
5582-
TypePosition outermostPosition);
5582+
TypePosition outermostPosition,
5583+
bool wantNonDependentBound = true);
5584+
5585+
Type transformFn(Type type, Type existentialBaseType,
5586+
TypePosition initialPos);
5587+
55835588

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

lib/AST/GenericSignature.cpp

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -631,23 +631,101 @@ Type GenericSignatureImpl::getNonDependentUpperBounds(Type type) const {
631631
bool hasExplicitAnyObject = requiresClass(type);
632632

633633
llvm::SmallVector<Type, 2> types;
634+
635+
// Class Inheritence
636+
// If the class contains a type parameter that cannot be reduced,
637+
// try looking for a non-dependent superclass.
634638
if (Type superclass = getSuperclassBound(type)) {
635-
// If the class contains a type parameter, try looking for a non-dependent
636-
// superclass.
637-
while (superclass && superclass->hasTypeParameter()) {
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;
656+
}
638657
superclass = superclass->getSuperclass();
639658
}
640-
641659
if (superclass) {
642660
types.push_back(superclass);
643661
hasExplicitAnyObject = false;
644662
}
645663
}
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.
646668
for (auto *proto : getRequiredProtocols(type)) {
647669
if (proto->requiresClass())
648670
hasExplicitAnyObject = false;
671+
672+
auto *baseType = proto->getDeclaredInterfaceType()->castTo<ProtocolType>();
649673

650-
types.push_back(proto->getDeclaredInterfaceType());
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);
651729
}
652730

653731
auto constraint = ProtocolCompositionType::get(
@@ -674,8 +752,20 @@ Type GenericSignatureImpl::getDependentUpperBounds(Type type) const {
674752
// FIXME: If the superclass bound is implied by one of our protocols, we
675753
// shouldn't add it to the constraint type.
676754
if (Type superclass = getSuperclassBound(type)) {
677-
types.push_back(superclass);
678755
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+
}
679769
}
680770

681771
for (auto proto : getRequiredProtocols(type)) {

lib/Sema/CSDiagnostics.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8819,6 +8819,10 @@ bool MissingExplicitExistentialCoercion::fixItRequiresParens() const {
88198819

88208820
void MissingExplicitExistentialCoercion::fixIt(
88218821
InFlightDiagnostic &diagnostic) const {
8822+
8823+
if (ErasedResultType->hasDependentMember())
8824+
return;
8825+
88228826
bool requiresParens = fixItRequiresParens();
88238827

88248828
auto callRange = getSourceRange();

lib/Sema/CSFix.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2503,8 +2503,10 @@ bool AddExplicitExistentialCoercion::isRequired(
25032503
if (!existentialType)
25042504
return Action::SkipChildren;
25052505

2506+
// let's grab the dependent upper bound here instead of nondependent
25062507
auto erasedMemberTy = typeEraseOpenedExistentialReference(
2507-
Type(member), *existentialType, typeVar, TypePosition::Covariant);
2508+
Type(member), *existentialType, typeVar, TypePosition::Covariant,
2509+
false);
25082510

25092511
// If result is an existential type and the base has `where` clauses
25102512
// associated with its associated types, the call needs a coercion.
@@ -2514,6 +2516,18 @@ bool AddExplicitExistentialCoercion::isRequired(
25142516
return Action::Stop;
25152517
}
25162518

2519+
if (erasedMemberTy->isExistentialType() &&
2520+
(erasedMemberTy->hasDependentMember() ||
2521+
erasedMemberTy->hasTypeParameter())) {
2522+
RequiresCoercion = true;
2523+
return Action::Stop;
2524+
}
2525+
2526+
if (erasedMemberTy->hasTypeVariable()) {
2527+
RequiresCoercion = true;
2528+
return Action::Stop;
2529+
}
2530+
25172531
return Action::SkipChildren;
25182532
}
25192533

@@ -2561,6 +2575,7 @@ bool AddExplicitExistentialCoercion::isRequired(
25612575
case RequirementKind::Layout: {
25622576
if (isAnchoredOn(req.getFirstType(), member->getAssocType()))
25632577
return true;
2578+
25642579
break;
25652580
}
25662581

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12839,6 +12839,7 @@ ConstraintSystem::simplifyApplicableFnConstraint(
1283912839
// `as` coercion.
1284012840
if (AddExplicitExistentialCoercion::isRequired(
1284112841
*this, func2->getResult(), openedExistentials, locator)) {
12842+
1284212843
if (!shouldAttemptFixes())
1284312844
return SolutionKind::Error;
1284412845

lib/Sema/ConstraintSystem.cpp

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2054,9 +2054,9 @@ static bool isMainDispatchQueueMember(ConstraintLocator *locator) {
20542054
/// \note If a 'Self'-rooted type parameter is bound to a concrete type, this
20552055
/// routine will recurse into the concrete type.
20562056
static Type
2057-
typeEraseExistentialSelfReferences(
2058-
Type refTy, Type baseTy,
2059-
TypePosition outermostPosition) {
2057+
typeEraseExistentialSelfReferences(Type refTy, Type baseTy,
2058+
TypePosition outermostPosition,
2059+
bool wantNonDependentBound = true) {
20602060
assert(baseTy->isExistentialType());
20612061
if (!refTy->hasTypeParameter()) {
20622062
return refTy;
@@ -2159,7 +2159,9 @@ typeEraseExistentialSelfReferences(
21592159
if (t->is<GenericTypeParamType>()) {
21602160
erasedTy = baseTy;
21612161
} else {
2162-
erasedTy = existentialSig->getNonDependentUpperBounds(t);
2162+
erasedTy = wantNonDependentBound
2163+
? existentialSig->getNonDependentUpperBounds(t)
2164+
: existentialSig->getDependentUpperBounds(t);
21632165
}
21642166

21652167
if (metatypeDepth) {
@@ -2170,13 +2172,12 @@ typeEraseExistentialSelfReferences(
21702172
return erasedTy;
21712173
});
21722174
};
2173-
21742175
return transformFn(refTy, outermostPosition);
21752176
}
21762177

21772178
Type constraints::typeEraseOpenedExistentialReference(
21782179
Type type, Type existentialBaseType, TypeVariableType *openedTypeVar,
2179-
TypePosition outermostPosition) {
2180+
TypePosition outermostPosition, bool wantNonDependentBound) {
21802181
Type selfGP = GenericTypeParamType::get(false, 0, 0, type->getASTContext());
21812182

21822183
// First, temporarily reconstitute the 'Self' generic parameter.
@@ -2193,8 +2194,8 @@ Type constraints::typeEraseOpenedExistentialReference(
21932194
});
21942195

21952196
// Then, type-erase occurrences of covariant 'Self'-rooted type parameters.
2196-
type = typeEraseExistentialSelfReferences(type, existentialBaseType,
2197-
outermostPosition);
2197+
type = typeEraseExistentialSelfReferences(
2198+
type, existentialBaseType, outermostPosition, wantNonDependentBound);
21982199

21992200
// Finally, swap the 'Self'-corresponding type variable back in.
22002201
return type.transformRec([&](TypeBase *t) -> Optional<Type> {
@@ -2299,16 +2300,25 @@ Type ConstraintSystem::getMemberReferenceTypeFromOpenedType(
22992300
const auto selfGP = cast<GenericTypeParamType>(
23002301
outerDC->getSelfInterfaceType()->getCanonicalType());
23012302
auto openedTypeVar = replacements.lookup(selfGP);
2303+
23022304
type = typeEraseOpenedExistentialReference(type, baseObjTy, openedTypeVar,
23032305
TypePosition::Covariant);
23042306

2307+
Type contextualTy;
2308+
2309+
if (auto *anchor = getAsExpr(simplifyLocatorToAnchor(locator))) {
2310+
contextualTy =
2311+
getContextualType(getParentExpr(anchor), /*forConstraint=*/false);
2312+
}
2313+
23052314
if (!hasFixFor(locator) &&
23062315
AddExplicitExistentialCoercion::isRequired(
23072316
*this, nonErasedResultTy,
23082317
[&](TypeVariableType *typeVar) {
23092318
return openedTypeVar == typeVar ? baseObjTy : Optional<Type>();
23102319
},
2311-
locator)) {
2320+
locator) &&
2321+
!contextualTy) {
23122322
recordFix(AddExplicitExistentialCoercion::create(
23132323
*this, getResultType(type), locator));
23142324
}

test/Constraints/opened_existentials.swift

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,8 @@ func testExplicitCoercionRequirement(v: any B, otherV: any B & D) {
255255

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
258-
259-
_ = getNoError(v) // Ok because T.C.A == Double
258+
// 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}}
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
@@ -305,3 +305,52 @@ func testExplicitCoercionRequirement(v: any B, otherV: any B & D) {
305305
getP((getC(v) as any P)) // Ok - parens avoid opening suppression
306306
getP((v.getC() as any P)) // Ok - parens avoid opening suppression
307307
}
308+
309+
// Generic Class Types
310+
class C1 {}
311+
class C2<T>: C1 {}
312+
313+
// Test Associated Types
314+
protocol P1 {
315+
associatedtype A
316+
associatedtype B: C2<A>
317+
318+
func returnAssocTypeB() -> B
319+
}
320+
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}}
323+
}
324+
325+
// Test Primary Associated Types
326+
protocol P2<A> {
327+
associatedtype A
328+
associatedtype B: C2<A>
329+
330+
func returnAssocTypeB() -> B
331+
}
332+
333+
func testAssocReturn(p: any P2<Int>) { // should return C2<A>
334+
let _ = p.returnAssocTypeB()
335+
}
336+
337+
func testAssocReturn(p: any P2<any P2<String>>) {
338+
let _ = p.returnAssocTypeB()
339+
}
340+
341+
protocol P3<A> {
342+
associatedtype A
343+
associatedtype B: C2<A>
344+
345+
func returnPrimaryAssocTypeA() -> A
346+
func returnAssocTypeCollection() -> any Collection<A>
347+
}
348+
349+
// Confirm there is no way to access Primary Associated Type
350+
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}}
352+
}
353+
354+
func testPrimaryAssocCollection(p: any P3<Float>) {
355+
let _: any Collection<Float> = p.returnAssocTypeCollection()
356+
}

test/decl/protocol/existential_member_accesses_self_assoctype.swift

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,7 @@ 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}}
640641
exist.method2(false) // expected-error {{instance method 'method2' requires that 'Self.A' conform to 'InvalidTypeParameters'}}
641642
exist.method3(false, false) // expected-error {{instance method 'method3' requires that 'Self.A' conform to 'InvalidTypeParameters'}}
642643
// expected-error@-1 {{member 'method3' cannot be used on value of type 'any InvalidTypeParameters'; consider using a generic constraint instead}}
@@ -794,14 +795,14 @@ do {
794795

795796
let _: (
796797
Struct<Bool>, (any ConcreteAssocTypes).Type, () -> Bool
797-
) -> any Class<Struct<Bool>.Inner> & ConcreteAssocTypes = arg.method4
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}}
798799

799800
let _: (
800801
Struct<Bool>, (any ConcreteAssocTypes).Type, () -> Bool
801802
) -> any Class<Struct<Bool>.Inner> & ConcreteAssocTypes = arg.property4
802803

803804
let _: any Class<Struct<Bool>.Inner> & ConcreteAssocTypes =
804-
arg[
805+
arg[ // expected-error {{inferred result type 'any Class<Struct<(any ConcreteAssocTypes).A7>.Inner> & ConcreteAssocTypes' requires explicit coercion due to loss of generic requirements}}
805806
subscript4: Struct<Bool>(), (any ConcreteAssocTypes).self, { true }
806807
]
807808
}
@@ -918,12 +919,26 @@ do {
918919
let _: Class2Base = exist.method5()
919920
let _: any Class2Base & CovariantAssocTypeErasure = exist.method6()
920921
let _: any Class2Base & CovariantAssocTypeErasure = exist.method7()
921-
922922
let _: Any? = exist.method8()
923923
let _: (AnyObject, Bool) = exist.method9()
924924
let _: any CovariantAssocTypeErasure.Type = exist.method10()
925925
let _: Array<Class2Base> = exist.method11()
926926
let _: Dictionary<String, Class2Base> = exist.method12()
927+
928+
let _ = exist.method1()
929+
let _ = exist.method2()
930+
let _ = exist.method3()
931+
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}}
933+
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}}
935+
let _ = exist.method8()
936+
let _ = exist.method9()
937+
let _ = exist.method10()
938+
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+
927942
}
928943
do {
929944
let exist: any CovariantAssocTypeErasureDerived

0 commit comments

Comments
 (0)