Skip to content

Commit e46a0c5

Browse files
committed
AST: Refine name lookup rule for protocol extension 'where' clauses
We have two "levels" of name lookup, and the more primitive level is used by name lookup itself to avoid certain cycles. For example, extension binding, resolution of inheritance clauses, etc. One interesting case is that a protocol extension can impose additional requiremnts on `Self`, and members of the right-hand side type are visible to unqualified lookup. The right-hand side of a `Self` requirement in this case is always a protocol type or class type canonically, but it might be written to refer to a protocol type alias. Before some changes for noncopyable generics, the primitive name lookup mechanism, implemented in directReferencesForTypeRepr() and such, would check if the TypeRepr had already been resolved by resolveType(). If so, it would immediately return the decl. This masked an issue, where the right-hand side of a `Self` requirement was resolved in the parent DeclContext. A more subtle rule is needed; for a protocol extension, we must resolve the right-hand side in the protocol, but disregard the protocol extension's `Self` requirements, because doing so would recursively trigger the same lookup again. Fixes rdar://problem/124498054.
1 parent 8e69dc5 commit e46a0c5

File tree

4 files changed

+114
-64
lines changed

4 files changed

+114
-64
lines changed

include/swift/AST/NameLookup.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,9 @@ enum class UnqualifiedLookupFlags {
247247
MacroLookup = 1 << 7,
248248
/// This lookup should only return modules
249249
ModuleLookup = 1 << 8,
250+
/// This lookup should discard 'Self' requirements in protocol extension
251+
/// 'where' clauses.
252+
DisregardSelfBounds = 1 << 9
250253
};
251254

252255
using UnqualifiedLookupOptions = OptionSet<UnqualifiedLookupFlags>;

lib/AST/NameLookup.cpp

Lines changed: 68 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,7 +1086,8 @@ namespace {
10861086
static DirectlyReferencedTypeDecls
10871087
directReferencesForTypeRepr(Evaluator &evaluator, ASTContext &ctx,
10881088
TypeRepr *typeRepr, DeclContext *dc,
1089-
bool allowUsableFromInline=false);
1089+
bool allowUsableFromInline,
1090+
bool rhsOfSelfRequirement);
10901091

10911092
/// Retrieve the set of type declarations that are directly referenced from
10921093
/// the given type.
@@ -1118,13 +1119,6 @@ SelfBounds SelfBoundsFromWhereClauseRequest::evaluate(
11181119
const DeclContext *dc =
11191120
protoDecl ? (const DeclContext *)protoDecl : (const DeclContext *)extDecl;
11201121

1121-
// A protocol or extension 'where' clause can reference associated types of
1122-
// the protocol itself, so we have to start unqualified lookup from 'dc'.
1123-
//
1124-
// However, the right hand side of a 'Self' conformance constraint must be
1125-
// resolved before unqualified lookup into 'dc' can work, so we make an
1126-
// exception here and begin lookup from the parent context instead.
1127-
auto *lookupDC = dc->getParent();
11281122
auto requirements = protoDecl ? protoDecl->getTrailingWhereClause()
11291123
: extDecl->getTrailingWhereClause();
11301124

@@ -1151,7 +1145,10 @@ SelfBounds SelfBoundsFromWhereClauseRequest::evaluate(
11511145
// Resolve the right-hand side.
11521146
DirectlyReferencedTypeDecls rhsDecls;
11531147
if (auto typeRepr = req.getConstraintRepr()) {
1154-
rhsDecls = directReferencesForTypeRepr(evaluator, ctx, typeRepr, lookupDC);
1148+
rhsDecls = directReferencesForTypeRepr(evaluator, ctx, typeRepr,
1149+
const_cast<DeclContext *>(dc),
1150+
/*allowUsableFromInline=*/false,
1151+
/*rhsOfSelfRequirement=*/true);
11551152
}
11561153

11571154
SmallVector<ModuleDecl *, 2> modulesFound;
@@ -1213,7 +1210,9 @@ TypeDeclsFromWhereClauseRequest::evaluate(Evaluator &evaluator,
12131210

12141211
DirectlyReferencedTypeDecls result;
12151212
auto resolve = [&](TypeRepr *typeRepr) {
1216-
auto decls = directReferencesForTypeRepr(evaluator, ctx, typeRepr, ext);
1213+
auto decls = directReferencesForTypeRepr(evaluator, ctx, typeRepr, ext,
1214+
/*allowUsableFromInline=*/false,
1215+
/*rhsOfSelfRequirement=*/false);
12171216
result.first.insert(result.first.end(),
12181217
decls.first.begin(),
12191218
decls.first.end());
@@ -2834,7 +2833,8 @@ static DirectlyReferencedTypeDecls
28342833
directReferencesForUnqualifiedTypeLookup(DeclNameRef name,
28352834
SourceLoc loc, DeclContext *dc,
28362835
LookupOuterResults lookupOuter,
2837-
bool allowUsableFromInline=false) {
2836+
bool allowUsableFromInline,
2837+
bool rhsOfSelfRequirement) {
28382838
UnqualifiedLookupOptions options =
28392839
UnqualifiedLookupFlags::TypeLookup |
28402840
UnqualifiedLookupFlags::AllowProtocolMembers;
@@ -2856,23 +2856,17 @@ directReferencesForUnqualifiedTypeLookup(DeclNameRef name,
28562856
//
28572857
// extension MyProto where AssocType == Int { ... }
28582858
//
2859-
// For this reason, ASTScope maps source locations inside the 'where'
2860-
// clause to a scope that performs the lookup into the protocol or
2861-
// protocol extension.
2862-
//
2863-
// However, protocol and protocol extensions can also put bounds on 'Self',
2864-
// for example:
2865-
//
2866-
// protocol MyProto where Self : MyClass { ... }
2867-
//
2868-
// We must start searching for 'MyClass' at the top level, otherwise
2869-
// we end up with a cycle, because qualified lookup wants to resolve
2870-
// 'Self' bounds to build the set of declarations to search inside of.
2871-
//
2872-
// To make this work, we handle the top-level lookup case explicitly
2873-
// here, bypassing unqualified lookup and ASTScope altogether.
2874-
if (dc->isModuleScopeContext())
2875-
loc = SourceLoc();
2859+
// To avoid cycles when resolving the right-hand side, we perform the
2860+
// lookup in the parent context (for a protocol), or a special mode where
2861+
// we disregard 'Self' requirements (for a protocol extension).
2862+
if (rhsOfSelfRequirement) {
2863+
if (dc->getExtendedProtocolDecl())
2864+
options |= UnqualifiedLookupFlags::DisregardSelfBounds;
2865+
else {
2866+
dc = dc->getModuleScopeContext();
2867+
loc = SourceLoc();
2868+
}
2869+
}
28762870

28772871
DirectlyReferencedTypeDecls results;
28782872

@@ -2956,10 +2950,12 @@ directReferencesForQualifiedTypeLookup(Evaluator &evaluator,
29562950
static DirectlyReferencedTypeDecls
29572951
directReferencesForDeclRefTypeRepr(Evaluator &evaluator, ASTContext &ctx,
29582952
DeclRefTypeRepr *repr, DeclContext *dc,
2959-
bool allowUsableFromInline) {
2953+
bool allowUsableFromInline,
2954+
bool rhsOfSelfRequirement) {
29602955
if (auto *qualIdentTR = dyn_cast<QualifiedIdentTypeRepr>(repr)) {
29612956
auto result = directReferencesForTypeRepr(
2962-
evaluator, ctx, qualIdentTR->getBase(), dc, allowUsableFromInline);
2957+
evaluator, ctx, qualIdentTR->getBase(), dc,
2958+
allowUsableFromInline, rhsOfSelfRequirement);
29632959

29642960
// For a qualified identifier, perform qualified name lookup.
29652961
result.first = directReferencesForQualifiedTypeLookup(
@@ -2972,13 +2968,14 @@ directReferencesForDeclRefTypeRepr(Evaluator &evaluator, ASTContext &ctx,
29722968
// For an unqualified identifier, perform unqualified name lookup.
29732969
return directReferencesForUnqualifiedTypeLookup(
29742970
repr->getNameRef(), repr->getLoc(), dc, LookupOuterResults::Excluded,
2975-
allowUsableFromInline);
2971+
allowUsableFromInline, rhsOfSelfRequirement);
29762972
}
29772973

29782974
static DirectlyReferencedTypeDecls
29792975
directReferencesForTypeRepr(Evaluator &evaluator,
29802976
ASTContext &ctx, TypeRepr *typeRepr,
2981-
DeclContext *dc, bool allowUsableFromInline) {
2977+
DeclContext *dc, bool allowUsableFromInline,
2978+
bool rhsOfSelfRequirement) {
29822979
DirectlyReferencedTypeDecls result;
29832980

29842981
switch (typeRepr->getKind()) {
@@ -2990,15 +2987,17 @@ directReferencesForTypeRepr(Evaluator &evaluator,
29902987
auto attributed = cast<AttributedTypeRepr>(typeRepr);
29912988
return directReferencesForTypeRepr(evaluator, ctx,
29922989
attributed->getTypeRepr(), dc,
2993-
allowUsableFromInline);
2990+
allowUsableFromInline,
2991+
rhsOfSelfRequirement);
29942992
}
29952993

29962994
case TypeReprKind::Composition: {
29972995
auto composition = cast<CompositionTypeRepr>(typeRepr);
29982996
for (auto component : composition->getTypes()) {
29992997
auto componentResult =
30002998
directReferencesForTypeRepr(evaluator, ctx, component, dc,
3001-
allowUsableFromInline);
2999+
allowUsableFromInline,
3000+
rhsOfSelfRequirement);
30023001
result.first.insert(result.first.end(),
30033002
componentResult.first.begin(),
30043003
componentResult.first.end());
@@ -3013,7 +3012,8 @@ directReferencesForTypeRepr(Evaluator &evaluator,
30133012
case TypeReprKind::UnqualifiedIdent:
30143013
return directReferencesForDeclRefTypeRepr(evaluator, ctx,
30153014
cast<DeclRefTypeRepr>(typeRepr),
3016-
dc, allowUsableFromInline);
3015+
dc, allowUsableFromInline,
3016+
rhsOfSelfRequirement);
30173017

30183018
case TypeReprKind::Dictionary:
30193019
result.first.push_back(ctx.getDictionaryDecl());
@@ -3024,7 +3024,8 @@ directReferencesForTypeRepr(Evaluator &evaluator,
30243024
if (tupleRepr->isParenType()) {
30253025
result = directReferencesForTypeRepr(evaluator, ctx,
30263026
tupleRepr->getElementType(0), dc,
3027-
allowUsableFromInline);
3027+
allowUsableFromInline,
3028+
rhsOfSelfRequirement);
30283029
} else {
30293030
result.first.push_back(ctx.getBuiltinTupleDecl());
30303031
}
@@ -3035,21 +3036,24 @@ directReferencesForTypeRepr(Evaluator &evaluator,
30353036
auto packExpansionRepr = cast<VarargTypeRepr>(typeRepr);
30363037
return directReferencesForTypeRepr(evaluator, ctx,
30373038
packExpansionRepr->getElementType(), dc,
3038-
allowUsableFromInline);
3039+
allowUsableFromInline,
3040+
rhsOfSelfRequirement);
30393041
}
30403042

30413043
case TypeReprKind::PackExpansion: {
30423044
auto packExpansionRepr = cast<PackExpansionTypeRepr>(typeRepr);
30433045
return directReferencesForTypeRepr(evaluator, ctx,
30443046
packExpansionRepr->getPatternType(), dc,
3045-
allowUsableFromInline);
3047+
allowUsableFromInline,
3048+
rhsOfSelfRequirement);
30463049
}
30473050

30483051
case TypeReprKind::PackElement: {
30493052
auto packReferenceRepr = cast<PackElementTypeRepr>(typeRepr);
30503053
return directReferencesForTypeRepr(evaluator, ctx,
30513054
packReferenceRepr->getPackType(), dc,
3052-
allowUsableFromInline);
3055+
allowUsableFromInline,
3056+
rhsOfSelfRequirement);
30533057
}
30543058

30553059
case TypeReprKind::Inverse: {
@@ -3058,7 +3062,8 @@ directReferencesForTypeRepr(Evaluator &evaluator,
30583062
auto *inverseRepr = cast<InverseTypeRepr>(typeRepr);
30593063
auto innerResult = directReferencesForTypeRepr(evaluator, ctx,
30603064
inverseRepr->getConstraint(), dc,
3061-
allowUsableFromInline);
3065+
allowUsableFromInline,
3066+
rhsOfSelfRequirement);
30623067
if (innerResult.first.size() == 1) {
30633068
if (auto *proto = dyn_cast<ProtocolDecl>(innerResult.first[0])) {
30643069
if (auto ip = proto->getInvertibleProtocolKind()) {
@@ -3169,7 +3174,9 @@ DirectlyReferencedTypeDecls InheritedDeclsReferencedRequest::evaluate(
31693174
dc = (DeclContext *)decl.get<const ExtensionDecl *>();
31703175

31713176
return directReferencesForTypeRepr(evaluator, dc->getASTContext(), typeRepr,
3172-
const_cast<DeclContext *>(dc));
3177+
const_cast<DeclContext *>(dc),
3178+
/*allowUsableFromInline=*/false,
3179+
/*rhsOfSelfRequirement=*/false);
31733180
}
31743181

31753182
// Fall back to semantic types.
@@ -3188,7 +3195,9 @@ DirectlyReferencedTypeDecls UnderlyingTypeDeclsReferencedRequest::evaluate(
31883195
// Prefer syntactic information when we have it.
31893196
if (auto typeRepr = typealias->getUnderlyingTypeRepr()) {
31903197
return directReferencesForTypeRepr(evaluator, typealias->getASTContext(),
3191-
typeRepr, typealias);
3198+
typeRepr, typealias,
3199+
/*allowUsableFromInline=*/false,
3200+
/*rhsOfSelfRequirement=*/false);
31923201
}
31933202

31943203
// Fall back to semantic types.
@@ -3354,7 +3363,8 @@ ExtendedNominalRequest::evaluate(Evaluator &evaluator,
33543363
ASTContext &ctx = ext->getASTContext();
33553364
DirectlyReferencedTypeDecls referenced =
33563365
directReferencesForTypeRepr(evaluator, ctx, typeRepr, ext->getParent(),
3357-
ext->isInSpecializeExtensionContext());
3366+
ext->isInSpecializeExtensionContext(),
3367+
/*rhsOfSelfRequirement=*/false);
33583368

33593369
// Resolve those type declarations to nominal type declarations.
33603370
SmallVector<ModuleDecl *, 2> modulesFound;
@@ -3402,7 +3412,9 @@ static bool declsAreProtocols(ArrayRef<TypeDecl *> decls) {
34023412

34033413
bool TypeRepr::isProtocolOrProtocolComposition(DeclContext *dc) {
34043414
auto &ctx = dc->getASTContext();
3405-
auto references = directReferencesForTypeRepr(ctx.evaluator, ctx, this, dc);
3415+
auto references = directReferencesForTypeRepr(ctx.evaluator, ctx, this, dc,
3416+
/*allowUsableFromInline=*/false,
3417+
/*rhsOfSelfRequirement=*/false);
34063418
return declsAreProtocols(references.first);
34073419
}
34083420

@@ -3435,7 +3447,9 @@ createTupleExtensionGenericParams(ASTContext &ctx,
34353447
DirectlyReferencedTypeDecls referenced =
34363448
directReferencesForTypeRepr(ctx.evaluator, ctx,
34373449
extendedTypeRepr,
3438-
ext->getParent());
3450+
ext->getParent(),
3451+
/*allowUsableFromInline=*/false,
3452+
/*rhsOfSelfRequirement=*/false);
34393453
assert(referenced.second.empty() && "Implement me");
34403454
if (referenced.first.size() != 1 || !isa<TypeAliasDecl>(referenced.first[0]))
34413455
return nullptr;
@@ -3708,7 +3722,9 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator,
37083722
DirectlyReferencedTypeDecls decls;
37093723
if (auto *typeRepr = attr->getTypeRepr()) {
37103724
decls = directReferencesForTypeRepr(
3711-
evaluator, ctx, typeRepr, dc);
3725+
evaluator, ctx, typeRepr, dc,
3726+
/*allowUsableFromInline=*/false,
3727+
/*rhsOfSelfRequirement=*/false);
37123728
} else if (Type type = attr->getType()) {
37133729
decls = directReferencesForType(type);
37143730
}
@@ -3737,7 +3753,9 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator,
37373753
modulesFound.clear();
37383754
anyObject = false;
37393755
decls = directReferencesForUnqualifiedTypeLookup(
3740-
name, loc, dc, LookupOuterResults::Included);
3756+
name, loc, dc, LookupOuterResults::Included,
3757+
/*allowUsableFromInline=*/false,
3758+
/*rhsOfSelfRequirement=*/false);
37413759
nominals = resolveTypeDeclsToNominal(evaluator, ctx, decls.first,
37423760
ResolveToNominalOptions(),
37433761
modulesFound, anyObject);
@@ -3970,7 +3988,9 @@ ProtocolDecl *ImplementsAttrProtocolRequest::evaluate(
39703988

39713989
ASTContext &ctx = dc->getASTContext();
39723990
DirectlyReferencedTypeDecls referenced =
3973-
directReferencesForTypeRepr(evaluator, ctx, typeRepr, dc);
3991+
directReferencesForTypeRepr(evaluator, ctx, typeRepr, dc,
3992+
/*allowUsableFromInline=*/false,
3993+
/*rhsOfSelfRequirement=*/false);
39743994

39753995
// Resolve those type declarations to nominal type declarations.
39763996
SmallVector<ModuleDecl *, 2> modulesFound;

lib/AST/UnqualifiedLookup.cpp

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -730,31 +730,27 @@ bool ASTScopeDeclGatherer::consume(ArrayRef<ValueDecl *> valuesArg,
730730
return false;
731731
}
732732

733-
static SmallVector<NominalTypeDecl *, 2> findSelfBounds(const DeclContext *dc) {
734-
auto nominal = dc->getSelfNominalTypeDecl();
733+
// TODO: in future, migrate this functionality into ASTScopes
734+
bool ASTScopeDeclConsumerForUnqualifiedLookup::lookInMembers(
735+
const DeclContext *scopeDC) const {
736+
auto nominal = scopeDC->getSelfNominalTypeDecl();
735737
if (!nominal)
736-
return {};
738+
return false;
737739

738740
SmallVector<NominalTypeDecl *, 2> selfBounds;
739741
selfBounds.push_back(nominal);
740742

741743
// For a protocol extension, check whether there are additional "Self"
742744
// constraints that can affect name lookup.
743-
if (dc->getExtendedProtocolDecl()) {
744-
auto ext = cast<ExtensionDecl>(dc);
745-
auto bounds = getSelfBoundsFromWhereClause(ext);
746-
for (auto bound : bounds.decls)
747-
selfBounds.push_back(bound);
745+
if (!factory.options.contains(UnqualifiedLookupFlags::DisregardSelfBounds)) {
746+
if (scopeDC->getExtendedProtocolDecl()) {
747+
auto ext = cast<ExtensionDecl>(scopeDC);
748+
auto bounds = getSelfBoundsFromWhereClause(ext);
749+
for (auto bound : bounds.decls)
750+
selfBounds.push_back(bound);
751+
}
748752
}
749753

750-
return selfBounds;
751-
}
752-
753-
// TODO: in future, migrate this functionality into ASTScopes
754-
bool ASTScopeDeclConsumerForUnqualifiedLookup::lookInMembers(
755-
const DeclContext *scopeDC) const {
756-
auto selfBounds = findSelfBounds(scopeDC);
757-
758754
// We're looking for members of a type.
759755
//
760756
// If we started the looking from inside a scope where a 'self' parameter
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
protocol P1 {
4+
typealias A = P2
5+
}
6+
7+
protocol P2 {
8+
associatedtype B
9+
typealias A1 = Int
10+
func f1()
11+
}
12+
13+
extension P2 {
14+
typealias A2 = String
15+
func f2() {}
16+
}
17+
18+
extension P1 where Self: A {
19+
typealias B1 = A1
20+
typealias B2 = A2
21+
22+
func g() {
23+
f1()
24+
f2()
25+
}
26+
}
27+
28+
// This is terrible and we should ban it some day
29+
extension P1 where Self: A, B: Hashable {
30+
func h(_: Set<B>) {}
31+
}

0 commit comments

Comments
 (0)