Skip to content

Commit ec0b836

Browse files
committed
[CSFix] Check for constrained associated types via generic signature
Instead of checking for presence of `where` clause, let's use requirement signature and check whether any of the requires are anchored on a particular associated type.
1 parent f2f7082 commit ec0b836

File tree

2 files changed

+98
-23
lines changed

2 files changed

+98
-23
lines changed

lib/Sema/CSFix.cpp

Lines changed: 96 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/AST/Type.h"
2323
#include "swift/AST/Types.h"
2424
#include "swift/AST/ExistentialLayout.h"
25+
#include "swift/AST/RequirementSignature.h"
2526
#include "swift/Basic/SourceManager.h"
2627
#include "swift/Sema/ConstraintLocator.h"
2728
#include "swift/Sema/ConstraintSystem.h"
@@ -2234,7 +2235,7 @@ bool AddExplicitExistentialCoercion::isRequired(
22342235
// If result is an existential type and the base has `where` clauses
22352236
// associated with its associated types, the call needs a coercion.
22362237
if (erasedMemberTy->isExistentialType() &&
2237-
hasConstrainedAssociatedTypes(member)) {
2238+
hasConstrainedAssociatedTypes(member, *existentialType)) {
22382239
RequiresCoercion = true;
22392240
return Action::Stop;
22402241
}
@@ -2246,7 +2247,7 @@ bool AddExplicitExistentialCoercion::isRequired(
22462247
// e.g. `$T` or `[$T]`.
22472248
if (auto *typeVar = componentTy->getAs<TypeVariableType>()) {
22482249
if (auto existentialType = GetExistentialType(typeVar)) {
2249-
RequiresCoercion |= hasConstrainedAssociatedTypes(
2250+
RequiresCoercion |= hasAnyConstrainedAssociatedTypes(
22502251
(*existentialType)->getExistentialLayout());
22512252
return RequiresCoercion ? Action::Stop : Action::SkipChildren;
22522253
}
@@ -2256,19 +2257,105 @@ bool AddExplicitExistentialCoercion::isRequired(
22562257
}
22572258

22582259
private:
2259-
bool hasConstrainedAssociatedTypes(DependentMemberType *member) {
2260-
auto *assocType = member->getAssocType();
2260+
/// Check whether the given member type has any of its associated
2261+
/// types constrained by requirements associated with the given
2262+
/// existential type.
2263+
static bool hasConstrainedAssociatedTypes(DependentMemberType *member,
2264+
Type existentialTy) {
2265+
auto layout = existentialTy->getExistentialLayout();
2266+
for (auto *protocol : layout.getProtocols()) {
2267+
auto requirementSig = protocol->getRequirementSignature();
2268+
if (hasConstrainedAssociatedTypes(member,
2269+
requirementSig.getRequirements()))
2270+
return true;
2271+
}
2272+
return false;
2273+
}
22612274

2262-
// If this member has associated type requirements, we are done.
2263-
if (assocType->getTrailingWhereClause())
2264-
return true;
2275+
/// Check whether the given member type has any of its associated
2276+
/// types constrained by the given requirement set.
2277+
static bool
2278+
hasConstrainedAssociatedTypes(DependentMemberType *member,
2279+
ArrayRef<Requirement> requirements) {
2280+
for (const auto &req : requirements) {
2281+
switch (req.getKind()) {
2282+
case RequirementKind::Superclass:
2283+
case RequirementKind::Conformance:
2284+
case RequirementKind::Layout: {
2285+
if (isAnchoredOn(req.getFirstType(), member->getAssocType()))
2286+
return true;
2287+
break;
2288+
}
2289+
2290+
case RequirementKind::SameType: {
2291+
auto lhsTy = req.getFirstType();
2292+
auto rhsTy = req.getSecondType();
2293+
2294+
if (isAnchoredOn(lhsTy, member->getAssocType()) ||
2295+
isAnchoredOn(rhsTy, member->getAssocType()))
2296+
return true;
22652297

2266-
if (auto *DMT = member->getBase()->getAs<DependentMemberType>())
2267-
return hasConstrainedAssociatedTypes(DMT);
2298+
break;
2299+
}
2300+
}
2301+
}
22682302

22692303
return false;
22702304
}
22712305

2306+
/// Check whether any of the protocols mentioned in the given
2307+
/// existential layout have constraints associated with their
2308+
/// associated types via a `where` clause, for example:
2309+
/// `associatedtype A: P where A.B == Int`
2310+
static bool hasAnyConstrainedAssociatedTypes(ExistentialLayout layout) {
2311+
for (auto *protocol : layout.getProtocols()) {
2312+
auto requirementSig = protocol->getRequirementSignature();
2313+
for (const auto &req : requirementSig.getRequirements()) {
2314+
switch (req.getKind()) {
2315+
case RequirementKind::Conformance:
2316+
case RequirementKind::Layout:
2317+
case RequirementKind::Superclass: {
2318+
if (getMemberChainDepth(req.getFirstType()) > 1)
2319+
return true;
2320+
break;
2321+
}
2322+
2323+
case RequirementKind::SameType:
2324+
auto lhsTy = req.getFirstType();
2325+
auto rhsTy = req.getSecondType();
2326+
2327+
if (getMemberChainDepth(lhsTy) > 1 ||
2328+
getMemberChainDepth(rhsTy) > 1)
2329+
return true;
2330+
2331+
break;
2332+
}
2333+
}
2334+
}
2335+
return false;
2336+
}
2337+
2338+
/// Check whether the given type is a dependent member type and
2339+
/// is anchored on the given associated type e.g. type is `A.B.C`
2340+
/// and associated type is `A.B`.
2341+
static bool isAnchoredOn(Type type, AssociatedTypeDecl *assocTy,
2342+
unsigned depth = 0) {
2343+
if (auto *member = type->getAs<DependentMemberType>()) {
2344+
if (member->getAssocType() == assocTy)
2345+
return depth > 0;
2346+
2347+
return isAnchoredOn(member->getBase(), assocTy, depth + 1);
2348+
}
2349+
2350+
return false;
2351+
}
2352+
2353+
static unsigned getMemberChainDepth(Type type, unsigned currDepth = 0) {
2354+
if (auto *memberTy = type->getAs<DependentMemberType>())
2355+
return getMemberChainDepth(memberTy->getBase(), currDepth + 1);
2356+
return currDepth;
2357+
}
2358+
22722359
static Type getBaseTypeOfDependentMemberChain(DependentMemberType *member) {
22732360
if (!member->getBase())
22742361
return member;
@@ -2280,17 +2367,6 @@ bool AddExplicitExistentialCoercion::isRequired(
22802367

22812368
return base;
22822369
}
2283-
2284-
static bool hasConstrainedAssociatedTypes(ExistentialLayout layout) {
2285-
for (auto *protocol : layout.getProtocols()) {
2286-
if (llvm::any_of(protocol->getAssociatedTypeMembers(),
2287-
[](const auto *assocTypeDecl) {
2288-
return bool(assocTypeDecl->getTrailingWhereClause());
2289-
}))
2290-
return true;
2291-
}
2292-
return false;
2293-
}
22942370
};
22952371

22962372
// First, let's check whether coercion is already there.

test/decl/protocol/existential_member_accesses_self_assoctype.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ protocol MiscTestsProto {
694694
do {
695695
func miscTests(_ arg: any MiscTestsProto) {
696696
var r: any Sequence & IteratorProtocol = arg.getR()
697-
r.makeIterator() // expected-warning {{result of call to 'makeIterator()' is unused}}
697+
r.makeIterator() // expected-error {{inferred result type 'any IteratorProtocol' requires explicit coercion due to loss of generic requirements}} {{19-19=as any IteratorProtocol}}
698698
r.next() // expected-warning {{result of call to 'next()' is unused}}
699699
r.nonexistent() // expected-error {{value of type 'any IteratorProtocol & Sequence' has no member 'nonexistent'}}
700700

@@ -786,8 +786,7 @@ do {
786786
_ = arg.method3 // expected-error {{member 'method3' cannot be used on value of type 'any ConcreteAssocTypes'; consider using a generic constraint instead}}
787787
_ = arg.property1 // expected-error {{member 'property1' cannot be used on value of type 'any ConcreteAssocTypes'; consider using a generic constraint instead}}
788788
// Covariant 'Self' erasure works in conjunction with concrete associated types.
789-
let _: (Bool, any ConcreteAssocTypes) = arg.property2 // expected-error {{inferred result type '(Bool, any ConcreteAssocTypes)' requires explicit coercion due to loss of generic requirements}} {{58-58=as (Bool, any ConcreteAssocTypes)}}
790-
let _: (Bool, any ConcreteAssocTypes) = arg.property2 as (Bool, any ConcreteAssocTypes) // Ok
789+
let _: (Bool, any ConcreteAssocTypes) = arg.property2 // ok
791790
_ = arg.property3 // expected-error {{member 'property3' cannot be used on value of type 'any ConcreteAssocTypes'; consider using a generic constraint instead}}
792791
_ = arg[subscript1: false] // expected-error {{member 'subscript' cannot be used on value of type 'any ConcreteAssocTypes'; consider using a generic constraint instead}}
793792
_ = arg[subscript2: false] // expected-error {{member 'subscript' cannot be used on value of type 'any ConcreteAssocTypes'; consider using a generic constraint instead}}

0 commit comments

Comments
 (0)