Skip to content

Commit ad445d0

Browse files
authored
Merge pull request #4143 from slavapestov/name-lookup-cleanup
Sema: Clean up name lookup and fix a couple of bugs
2 parents 7fa18f3 + d5c2f44 commit ad445d0

File tree

9 files changed

+114
-86
lines changed

9 files changed

+114
-86
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2767,6 +2767,11 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
27672767
if (includeInaccessibleMembers)
27682768
lookupOptions |= NameLookupFlags::IgnoreAccessibility;
27692769

2770+
// If a constructor is only visible as a witness for a protocol
2771+
// requirement, it must be an invalid override. Also, protocol
2772+
// extensions cannot yet define designated initializers.
2773+
lookupOptions -= NameLookupFlags::PerformConformanceCheck;
2774+
27702775
LookupResult ctors = TC.lookupConstructors(DC, baseObjTy, lookupOptions);
27712776
if (!ctors)
27722777
return result; // No result.

lib/Sema/TypeCheckDecl.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5369,8 +5369,18 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
53695369
}
53705370

53715371
if (members.empty()) {
5372-
NameLookupOptions lookupOptions =
5373-
defaultMemberLookupOptions - NameLookupFlags::DynamicLookup;
5372+
auto lookupOptions = defaultMemberLookupOptions;
5373+
5374+
// Class methods cannot override declarations only
5375+
// visible via dynamic dispatch.
5376+
lookupOptions -= NameLookupFlags::DynamicLookup;
5377+
5378+
// Class methods cannot override declarations only
5379+
// visible as protocol requirements or protocol
5380+
// extension members.
5381+
lookupOptions -= NameLookupFlags::ProtocolMembers;
5382+
lookupOptions -= NameLookupFlags::PerformConformanceCheck;
5383+
53745384
members = TC.lookupMember(decl->getDeclContext(), superclassMetaTy,
53755385
name, lookupOptions);
53765386
}

lib/Sema/TypeCheckNameLookup.cpp

Lines changed: 37 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ namespace {
3838
LookupResult &Result;
3939
DeclContext *DC;
4040
NameLookupOptions Options;
41-
bool ConsiderProtocolMembers;
42-
bool SearchingFromProtoExt;
4341
bool IsMemberLookup;
4442

4543
/// The vector of found declarations.
@@ -50,11 +48,9 @@ namespace {
5048

5149
public:
5250
LookupResultBuilder(TypeChecker &tc, LookupResult &result, DeclContext *dc,
53-
NameLookupOptions options, bool considerProtocolMembers,
54-
bool searchingFromProtoExt, bool isMemberLookup)
51+
NameLookupOptions options,
52+
bool isMemberLookup)
5553
: TC(tc), Result(result), DC(dc), Options(options),
56-
ConsiderProtocolMembers(considerProtocolMembers),
57-
SearchingFromProtoExt(searchingFromProtoExt),
5854
IsMemberLookup(isMemberLookup) { }
5955

6056
~LookupResultBuilder() {
@@ -118,67 +114,66 @@ namespace {
118114
DeclContext *foundDC = found->getDeclContext();
119115
auto foundProto = foundDC->getAsProtocolOrProtocolExtensionContext();
120116

121-
// Determine the nominal type through which we found the
122-
// declaration.
123-
NominalTypeDecl *baseNominal = nullptr;
124-
if (!base) {
125-
// Nothing to do.
126-
} else if (auto baseParam = dyn_cast<ParamDecl>(base)) {
127-
auto baseDC = baseParam->getDeclContext();
128-
if (isa<AbstractFunctionDecl>(baseDC))
129-
baseDC = baseDC->getParent();
130-
131-
baseNominal = baseDC->getAsNominalTypeOrNominalTypeExtensionContext();
132-
assert(baseNominal && "Did not find nominal type");
133-
} else {
134-
baseNominal = cast<NominalTypeDecl>(base);
135-
}
117+
auto addResult = [&](ValueDecl *result) {
118+
if (Known.insert({{result, base}, false}).second) {
119+
Result.add({result, base});
120+
FoundDecls.push_back(result);
121+
}
122+
};
136123

137124
// If this isn't a protocol member to be given special
138125
// treatment, just add the result.
139-
if (!ConsiderProtocolMembers ||
140-
!isa<ProtocolDecl>(found->getDeclContext()) ||
141-
SearchingFromProtoExt ||
126+
if (!Options.contains(NameLookupFlags::ProtocolMembers) ||
127+
!isa<ProtocolDecl>(foundDC) ||
142128
isa<GenericTypeParamDecl>(found) ||
143-
(isa<FuncDecl>(found) && cast<FuncDecl>(found)->isOperator())) {
144-
if (Known.insert({{found, base}, false}).second) {
145-
Result.add({found, base});
146-
FoundDecls.push_back(found);
147-
}
129+
(isa<FuncDecl>(found) && cast<FuncDecl>(found)->isOperator()) ||
130+
foundInType->isAnyExistentialType()) {
131+
addResult(found);
148132
return;
149133
}
150134

135+
assert(isa<ProtocolDecl>(foundDC));
136+
151137
// If we found something within the protocol itself, and our
152138
// search began somewhere that is not in a protocol or extension
153139
// thereof, remap this declaration to the witness.
154-
if (isa<ProtocolDecl>(foundDC) && !isa<ProtocolDecl>(baseNominal)) {
140+
if (foundInType->is<ArchetypeType>() ||
141+
Options.contains(NameLookupFlags::PerformConformanceCheck)) {
155142
// Dig out the protocol conformance.
156143
ProtocolConformance *conformance = nullptr;
157144
if (!TC.conformsToProtocol(foundInType, foundProto, DC,
158-
conformanceOptions, &conformance) ||
159-
!conformance)
145+
conformanceOptions, &conformance))
160146
return;
161147

148+
// We have an abstract conformance of an archetype to a protocol.
149+
// Just return the requirement.
150+
if (!conformance) {
151+
assert(foundInType->is<ArchetypeType>());
152+
addResult(found);
153+
return;
154+
}
155+
162156
// Dig out the witness.
163157
ValueDecl *witness;
164158
if (auto assocType = dyn_cast<AssociatedTypeDecl>(found)) {
165159
witness = conformance->getTypeWitnessSubstAndDecl(assocType, &TC)
166160
.second;
167161
} else if (isa<TypeAliasDecl>(found)) {
168-
// No witness for typealiases.
162+
// No witness for typealiases. This means typealiases in
163+
// protocols cannot be found if PerformConformanceCheck
164+
// is on.
165+
//
166+
// FIXME: Fix this.
169167
return;
170168
} else {
171169
witness = conformance->getWitness(found, &TC).getDecl();
172170
}
173171

174172
// FIXME: the "isa<ProtocolDecl>()" check will be wrong for
175173
// default implementations in protocols.
176-
if (witness && !isa<ProtocolDecl>(witness->getDeclContext())) {
177-
if (Known.insert({{witness, base}, false}).second) {
178-
Result.add({witness, base});
179-
FoundDecls.push_back(witness);
180-
}
181-
}
174+
if (witness && !isa<ProtocolDecl>(witness->getDeclContext()))
175+
addResult(witness);
176+
182177
return;
183178
}
184179
}
@@ -188,29 +183,14 @@ namespace {
188183
LookupResult TypeChecker::lookupUnqualified(DeclContext *dc, DeclName name,
189184
SourceLoc loc,
190185
NameLookupOptions options) {
191-
// Determine whether we're searching from a protocol extension.
192-
bool searchingFromProtoExt = false;
193-
for (auto outerDC = dc; outerDC; outerDC = outerDC->getParent()) {
194-
if (auto ext = dyn_cast<ExtensionDecl>(outerDC)) {
195-
if (ext->getExtendedType() && ext->getExtendedType()->is<ProtocolType>()) {
196-
searchingFromProtoExt = true;
197-
break;
198-
}
199-
}
200-
}
201-
202186
UnqualifiedLookup lookup(name, dc, this,
203187
options.contains(NameLookupFlags::KnownPrivate),
204188
loc,
205189
options.contains(NameLookupFlags::OnlyTypes),
206190
options.contains(NameLookupFlags::ProtocolMembers));
207191

208192
LookupResult result;
209-
bool considerProtocolMembers
210-
= options.contains(NameLookupFlags::ProtocolMembers);
211193
LookupResultBuilder builder(*this, result, dc, options,
212-
considerProtocolMembers,
213-
searchingFromProtoExt,
214194
/*memberLookup*/false);
215195
for (const auto &found : lookup.Results) {
216196
// Determine which type we looked through to find this result.
@@ -263,11 +243,7 @@ LookupResult TypeChecker::lookupMember(DeclContext *dc,
263243
}
264244
NominalTypeDecl *nominalLookupType = lookupType->getAnyNominal();
265245

266-
/// Whether to consider protocol members or not.
267-
bool considerProtocolMembers
268-
= nominalLookupType && !isa<ProtocolDecl>(nominalLookupType) &&
269-
options.contains(NameLookupFlags::ProtocolMembers);
270-
if (considerProtocolMembers)
246+
if (options.contains(NameLookupFlags::ProtocolMembers))
271247
subOptions |= NL_ProtocolMembers;
272248

273249
// We handle our own overriding/shadowing filtering.
@@ -282,14 +258,12 @@ LookupResult TypeChecker::lookupMember(DeclContext *dc,
282258
result.clear();
283259

284260
LookupResultBuilder builder(*this, result, dc, options,
285-
considerProtocolMembers,
286-
/*protocol extension*/false,
287-
/*member lookup*/true);
261+
/*memberLookup*/true);
288262
SmallVector<ValueDecl *, 4> lookupResults;
289263
dc->lookupQualified(type, name, subOptions, this, lookupResults);
290264

291265
for (auto found : lookupResults) {
292-
builder.add(found, nominalLookupType, type);
266+
builder.add(found, nominalLookupType, lookupType);
293267
}
294268
};
295269

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,13 +1110,17 @@ WitnessChecker::lookupValueWitnesses(ValueDecl *req, bool *ignoringNames) {
11101110
}
11111111
} else {
11121112
// Variable/function/subscript requirements.
1113-
auto metaType = MetatypeType::get(Adoptee);
1114-
auto candidates = TC.lookupMember(DC, metaType, req->getFullName());
1113+
auto lookupOptions = defaultMemberTypeLookupOptions;
1114+
lookupOptions -= NameLookupFlags::PerformConformanceCheck;
1115+
1116+
auto candidates = TC.lookupMember(DC, Adoptee, req->getFullName(),
1117+
lookupOptions);
11151118

11161119
// If we didn't find anything with the appropriate name, look
11171120
// again using only the base name.
11181121
if (candidates.empty() && ignoringNames) {
1119-
candidates = TC.lookupMember(DC, metaType, req->getName());
1122+
candidates = TC.lookupMember(DC, Adoptee, req->getName(),
1123+
lookupOptions);
11201124
*ignoringNames = true;
11211125
}
11221126

@@ -1960,12 +1964,12 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
19601964
TypeLoc::withoutLoc(type),
19611965
/*genericparams*/nullptr,
19621966
DC);
1963-
Type metaType = MetatypeType::get(type);
19641967
aliasDecl->computeType();
19651968
aliasDecl->setImplicit();
19661969
if (type->is<ErrorType>())
19671970
aliasDecl->setInvalid();
1968-
if (metaType->hasArchetype()) {
1971+
if (type->hasArchetype()) {
1972+
Type metaType = MetatypeType::get(type);
19691973
aliasDecl->setInterfaceType(
19701974
ArchetypeBuilder::mapTypeOutOfContext(DC, metaType));
19711975
}
@@ -2498,11 +2502,9 @@ static CheckTypeWitnessResult checkTypeWitness(TypeChecker &tc, DeclContext *dc,
24982502
/// Attempt to resolve a type witness via member name lookup.
24992503
ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
25002504
AssociatedTypeDecl *assocType) {
2501-
auto metaType = MetatypeType::get(Adoptee);
2502-
25032505
// Look for a member type with the same name as the associated type.
2504-
auto candidates = TC.lookupMemberType(DC, metaType, assocType->getName(),
2505-
None);
2506+
auto candidates = TC.lookupMemberType(DC, Adoptee, assocType->getName(),
2507+
/*lookupOptions=*/None);
25062508

25072509
// If there aren't any candidates, we're done.
25082510
if (!candidates) {

lib/Sema/TypeCheckStmt.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,8 +1347,15 @@ static bool checkSuperInit(TypeChecker &tc, ConstructorDecl *fromCtor,
13471347
// only one designated initializer.
13481348
if (implicitlyGenerated) {
13491349
auto superclassTy = ctor->getExtensionType();
1350-
NameLookupOptions lookupOptions
1351-
= defaultConstructorLookupOptions | NameLookupFlags::KnownPrivate;
1350+
auto lookupOptions = defaultConstructorLookupOptions;
1351+
lookupOptions |= NameLookupFlags::KnownPrivate;
1352+
1353+
// If a constructor is only visible as a witness for a protocol
1354+
// requirement, it must be an invalid override. Also, protocol
1355+
// extensions cannot yet define designated initializers.
1356+
lookupOptions -= NameLookupFlags::ProtocolMembers;
1357+
lookupOptions -= NameLookupFlags::PerformConformanceCheck;
1358+
13521359
for (auto member : tc.lookupConstructors(fromCtor, superclassTy,
13531360
lookupOptions)) {
13541361
auto superclassCtor = dyn_cast<ConstructorDecl>(member.Decl);

lib/Sema/TypeChecker.h

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -219,14 +219,22 @@ enum class NameLookupFlags {
219219
KnownPrivate = 0x01,
220220
/// Whether name lookup should be able to find protocol members.
221221
ProtocolMembers = 0x02,
222+
/// Whether we should map the requirement to the witness if we
223+
/// find a protocol member and the base type is a concrete type.
224+
///
225+
/// If this is not set but ProtocolMembers is set, we will
226+
/// find protocol extension members, but not protocol requirements
227+
/// that do not yet have a witness (such as inferred associated
228+
/// types, or witnesses for derived conformances).
229+
PerformConformanceCheck = 0x04,
222230
/// Whether to perform 'dynamic' name lookup that finds @objc
223231
/// members of any class or protocol.
224-
DynamicLookup = 0x04,
232+
DynamicLookup = 0x08,
225233
/// Whether we're only looking for types.
226-
OnlyTypes = 0x08,
234+
OnlyTypes = 0x10,
227235
/// Whether to ignore access control for this lookup, allowing inaccessible
228236
/// results to be returned.
229-
IgnoreAccessibility = 0x10,
237+
IgnoreAccessibility = 0x20,
230238
};
231239

232240
/// A set of options that control name lookup.
@@ -239,19 +247,24 @@ inline NameLookupOptions operator|(NameLookupFlags flag1,
239247

240248
/// Default options for member name lookup.
241249
const NameLookupOptions defaultMemberLookupOptions
242-
= NameLookupFlags::DynamicLookup | NameLookupFlags::ProtocolMembers;
250+
= NameLookupFlags::DynamicLookup |
251+
NameLookupFlags::ProtocolMembers |
252+
NameLookupFlags::PerformConformanceCheck;
243253

244254
/// Default options for constructor lookup.
245255
const NameLookupOptions defaultConstructorLookupOptions
246-
= NameLookupFlags::ProtocolMembers;
256+
= NameLookupFlags::ProtocolMembers |
257+
NameLookupFlags::PerformConformanceCheck;
247258

248259
/// Default options for member type lookup.
249260
const NameLookupOptions defaultMemberTypeLookupOptions
250-
= NameLookupFlags::ProtocolMembers;
261+
= NameLookupFlags::ProtocolMembers |
262+
NameLookupFlags::PerformConformanceCheck;
251263

252264
/// Default options for unqualified name lookup.
253265
const NameLookupOptions defaultUnqualifiedLookupOptions
254-
= NameLookupFlags::ProtocolMembers;
266+
= NameLookupFlags::ProtocolMembers |
267+
NameLookupFlags::PerformConformanceCheck;
255268

256269
/// Describes the result of comparing two entities, of which one may be better
257270
/// or worse than the other, or they are unordered.

test/Constraints/members.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,16 @@ extension P {
181181

182182
protocol ClassP : class {
183183
func bas(_ x: Int)
184+
func quux(_ x: Int)
185+
}
186+
187+
class ClassC : ClassP {
188+
func bas(_ x: Int) {}
189+
}
190+
191+
extension ClassP {
192+
func quux(_ x: Int) {}
193+
func bing(_ x: Int) {}
184194
}
185195

186196
func generic<T: P>(_ t: T) {
@@ -243,6 +253,14 @@ func genericClassP<T: ClassP>(_ t: T) {
243253
let _: () = id(T.bas(t)(1))
244254
}
245255

256+
func genericClassC<C : ClassC>(_ c: C) {
257+
// Make sure that we can find members of protocol extensions
258+
// on a class-bound archetype
259+
let _ = c.bas(123)
260+
let _ = c.quux(123)
261+
let _ = c.bing(123)
262+
}
263+
246264
////
247265
// Members of existentials
248266
////

test/Generics/Inputs/associated_types_multi_file_helper.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
protocol Fooable {
2-
associatedtype AssocType // expected-note {{did you mean 'AssocType'?}}
2+
associatedtype AssocType
33
func foo(x : AssocType)
44
}
55

test/Generics/associated_types_multi_file.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@
22

33
var x: X.AssocType = 0.0 as Float
44

5-
// FIXME: <rdar://problem/16123805> Inferred associated types can't be used in expression contexts
6-
var y = Y.AssocType() // expected-error{{type 'Y' has no member 'AssocType'}}
5+
var y = Y.AssocType()

0 commit comments

Comments
 (0)