Skip to content

Commit 32d8f8a

Browse files
authored
Merge pull request #18562 from DougGregor/remove-shadowed-without-validation
[Name lookup] Make shadowing less reliant on the type checker.
2 parents 1529d65 + 8d11ce1 commit 32d8f8a

File tree

7 files changed

+210
-157
lines changed

7 files changed

+210
-157
lines changed

lib/AST/NameLookup.cpp

Lines changed: 196 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -157,27 +157,165 @@ static ConstructorComparison compareConstructors(ConstructorDecl *ctor1,
157157
return ConstructorComparison::Same;
158158
}
159159

160-
bool swift::removeShadowedDecls(SmallVectorImpl<ValueDecl*> &decls,
161-
const ModuleDecl *curModule) {
162-
auto typeResolver = curModule->getASTContext().getLazyResolver();
163-
164-
// Category declarations by their signatures.
165-
llvm::SmallDenseMap<std::pair<CanType, DeclBaseName>,
166-
llvm::TinyPtrVector<ValueDecl *>>
167-
CollidingDeclGroups;
168-
169-
/// Objective-C initializers are tracked by their context type and
170-
/// full name.
171-
llvm::SmallDenseMap<std::pair<CanType, DeclName>,
172-
llvm::TinyPtrVector<ConstructorDecl *>>
173-
ObjCCollidingConstructors;
174-
bool anyCollisions = false;
160+
/// Given a set of declarations whose names and signatures have matched,
161+
/// figure out which of these declarations have been shadowed by others.
162+
static void recordShadowedDeclsAfterSignatureMatch(
163+
ArrayRef<ValueDecl *> decls,
164+
const ModuleDecl *curModule,
165+
llvm::SmallPtrSetImpl<ValueDecl *> &shadowed) {
166+
assert(decls.size() > 1 && "Nothing collided");
167+
168+
// Compare each declaration to every other declaration. This is
169+
// unavoidably O(n^2) in the number of declarations, but because they
170+
// all have the same signature, we expect n to remain small.
171+
ASTContext &ctx = curModule->getASTContext();
172+
for (unsigned firstIdx : indices(decls)) {
173+
auto firstDecl = decls[firstIdx];
174+
auto firstModule = firstDecl->getModuleContext();
175+
for (unsigned secondIdx : range(firstIdx + 1, decls.size())) {
176+
// Determine whether one module takes precedence over another.
177+
auto secondDecl = decls[secondIdx];
178+
auto secondModule = secondDecl->getModuleContext();
179+
180+
// If one declaration is in a protocol or extension thereof and the
181+
// other is not, prefer the one that is not.
182+
if ((bool)firstDecl->getDeclContext()
183+
->getAsProtocolOrProtocolExtensionContext()
184+
!= (bool)secondDecl->getDeclContext()
185+
->getAsProtocolOrProtocolExtensionContext()) {
186+
if (firstDecl->getDeclContext()
187+
->getAsProtocolOrProtocolExtensionContext()) {
188+
shadowed.insert(firstDecl);
189+
break;
190+
} else {
191+
shadowed.insert(secondDecl);
192+
continue;
193+
}
194+
}
195+
196+
// If one declaration is available and the other is not, prefer the
197+
// available one.
198+
if (firstDecl->getAttrs().isUnavailable(ctx) !=
199+
secondDecl->getAttrs().isUnavailable(ctx)) {
200+
if (firstDecl->getAttrs().isUnavailable(ctx)) {
201+
shadowed.insert(firstDecl);
202+
break;
203+
} else {
204+
shadowed.insert(secondDecl);
205+
continue;
206+
}
207+
}
208+
209+
// Don't apply module-shadowing rules to members of protocol types.
210+
if (isa<ProtocolDecl>(firstDecl->getDeclContext()) ||
211+
isa<ProtocolDecl>(secondDecl->getDeclContext()))
212+
continue;
213+
214+
// Prefer declarations in the current module over those in another
215+
// module.
216+
// FIXME: This is a hack. We should query a (lazily-built, cached)
217+
// module graph to determine shadowing.
218+
if ((firstModule == curModule) != (secondModule == curModule)) {
219+
// If the first module is the current module, the second declaration
220+
// is shadowed by the first.
221+
if (firstModule == curModule) {
222+
shadowed.insert(secondDecl);
223+
continue;
224+
}
225+
226+
// Otherwise, the first declaration is shadowed by the second. There is
227+
// no point in continuing to compare the first declaration to others.
228+
shadowed.insert(firstDecl);
229+
break;
230+
}
231+
232+
// Prefer declarations in an overlay to similar declarations in
233+
// the Clang module it customizes.
234+
if (firstDecl->hasClangNode() != secondDecl->hasClangNode()) {
235+
auto clangLoader = ctx.getClangModuleLoader();
236+
if (!clangLoader) continue;
237+
238+
if (clangLoader->isInOverlayModuleForImportedModule(
239+
firstDecl->getDeclContext(),
240+
secondDecl->getDeclContext())) {
241+
shadowed.insert(secondDecl);
242+
continue;
243+
}
244+
245+
if (clangLoader->isInOverlayModuleForImportedModule(
246+
secondDecl->getDeclContext(),
247+
firstDecl->getDeclContext())) {
248+
shadowed.insert(firstDecl);
249+
break;
250+
}
251+
}
252+
}
253+
}
254+
}
255+
256+
/// Look through the given set of declarations (that all have the same name),
257+
/// recording those that are shadowed by another declaration in the
258+
/// \c shadowed set.
259+
static void recordShadowDeclsAfterObjCInitMatch(
260+
ArrayRef<ConstructorDecl *> ctors,
261+
llvm::SmallPtrSetImpl<ValueDecl *> &shadowed) {
262+
assert(ctors.size() > 1 && "No collisions");
263+
264+
ASTContext &ctx = ctors.front()->getASTContext();
265+
266+
// Find the "best" constructor with this signature.
267+
ConstructorDecl *bestCtor = ctors[0];
268+
for (auto ctor : ctors.slice(1)) {
269+
auto comparison = compareConstructors(ctor, bestCtor, ctx);
270+
if (comparison == ConstructorComparison::Better)
271+
bestCtor = ctor;
272+
}
273+
274+
// Shadow any initializers that are worse.
275+
for (auto ctor : ctors) {
276+
auto comparison = compareConstructors(ctor, bestCtor, ctx);
277+
if (comparison == ConstructorComparison::Worse)
278+
shadowed.insert(ctor);
279+
}
280+
}
281+
282+
/// Look through the given set of declarations (that all have the same name),
283+
/// recording those that are shadowed by another declaration in the
284+
/// \c shadowed set.
285+
static void recordShadowedDecls(ArrayRef<ValueDecl *> decls,
286+
const ModuleDecl *curModule,
287+
llvm::SmallPtrSetImpl<ValueDecl *> &shadowed) {
288+
if (decls.size() < 2)
289+
return;
290+
291+
auto typeResolver = decls[0]->getASTContext().getLazyResolver();
292+
293+
// Categorize all of the declarations based on their overload signatures.
294+
llvm::SmallDenseMap<CanType, llvm::TinyPtrVector<ValueDecl *>> collisions;
295+
llvm::SmallVector<CanType, 2> collisionTypes;
296+
llvm::SmallDenseMap<NominalTypeDecl *, llvm::TinyPtrVector<ConstructorDecl *>>
297+
objCInitializerCollisions;
298+
llvm::TinyPtrVector<NominalTypeDecl *> objCInitializerCollisionNominals;
299+
175300
for (auto decl : decls) {
176-
// FIXME: Egregious hack to avoid failing when there are no declared types.
177-
// FIXME: Pass this down instead of getting it from the ASTContext.
301+
// Specifically keep track of Objective-C initializers, which can come from
302+
// either init methods or factory methods.
303+
if (decl->hasClangNode()) {
304+
if (auto ctor = dyn_cast<ConstructorDecl>(decl)) {
305+
auto nominal = ctor->getDeclContext()
306+
->getAsNominalTypeOrNominalTypeExtensionContext();
307+
auto &knownInits = objCInitializerCollisions[nominal];
308+
if (knownInits.size() == 1) {
309+
objCInitializerCollisionNominals.push_back(nominal);
310+
}
311+
knownInits.push_back(ctor);
312+
}
313+
}
314+
315+
// We need an interface type here.
178316
if (typeResolver)
179317
typeResolver->resolveDeclSignature(decl);
180-
318+
181319
// If the decl is currently being validated, this is likely a recursive
182320
// reference and we'll want to skip ahead so as to avoid having its type
183321
// attempt to desugar itself.
@@ -195,154 +333,60 @@ bool swift::removeShadowedDecls(SmallVectorImpl<ValueDecl*> &decls,
195333
if (auto asd = dyn_cast<AbstractStorageDecl>(decl))
196334
signature = asd->getOverloadSignatureType();
197335

198-
// If we've seen a declaration with this signature before, note it.
199-
auto &knownDecls =
200-
CollidingDeclGroups[std::make_pair(signature, decl->getBaseName())];
336+
// Record this declaration based on its signature.
337+
auto &known = collisions[signature];
338+
if (known.size() == 1) {
339+
collisionTypes.push_back(signature);
340+
}
341+
known.push_back(decl);
342+
}
343+
344+
// Check whether we have shadowing for signature collisions.
345+
for (auto signature : collisionTypes) {
346+
recordShadowedDeclsAfterSignatureMatch(collisions[signature], curModule,
347+
shadowed);
348+
}
349+
350+
// Check whether we have shadowing for Objective-C initializer collisions.
351+
for (auto nominal : objCInitializerCollisionNominals) {
352+
recordShadowDeclsAfterObjCInitMatch(objCInitializerCollisions[nominal],
353+
shadowed);
354+
}
355+
}
356+
357+
bool swift::removeShadowedDecls(SmallVectorImpl<ValueDecl*> &decls,
358+
const ModuleDecl *curModule) {
359+
// Collect declarations with the same (full) name.
360+
llvm::SmallDenseMap<DeclName, llvm::TinyPtrVector<ValueDecl *>>
361+
collidingDeclGroups;
362+
bool anyCollisions = false;
363+
for (auto decl : decls) {
364+
// Record this declaration based on its full name.
365+
auto &knownDecls = collidingDeclGroups[decl->getFullName()];
201366
if (!knownDecls.empty())
202367
anyCollisions = true;
203368

204369
knownDecls.push_back(decl);
205-
206-
// Specifically keep track of Objective-C initializers, which can come from
207-
// either init methods or factory methods.
208-
if (decl->hasClangNode()) {
209-
if (auto ctor = dyn_cast<ConstructorDecl>(decl)) {
210-
auto ctorSignature
211-
= std::make_pair(ctor->getDeclContext()->getDeclaredInterfaceType()
212-
->getCanonicalType(),
213-
decl->getFullName());
214-
auto &knownCtors = ObjCCollidingConstructors[ctorSignature];
215-
if (!knownCtors.empty())
216-
anyCollisions = true;
217-
knownCtors.push_back(ctor);
218-
}
219-
}
220370
}
221371

222-
// If there were no signature collisions, there is nothing to do.
372+
// If nothing collided, we're done.
223373
if (!anyCollisions)
224374
return false;
225375

226-
// Determine the set of declarations that are shadowed by other declarations.
376+
// Walk through the declarations again, marking any declarations that shadow.
227377
llvm::SmallPtrSet<ValueDecl *, 4> shadowed;
228-
ASTContext &ctx = decls[0]->getASTContext();
229-
for (auto &collidingDecls : CollidingDeclGroups) {
230-
// If only one declaration has this signature, it isn't shadowed by
231-
// anything.
232-
if (collidingDecls.second.size() == 1)
233-
continue;
234-
235-
// Compare each declaration to every other declaration. This is
236-
// unavoidably O(n^2) in the number of declarations, but because they
237-
// all have the same signature, we expect n to remain small.
238-
for (unsigned firstIdx = 0, n = collidingDecls.second.size();
239-
firstIdx != n; ++firstIdx) {
240-
auto firstDecl = collidingDecls.second[firstIdx];
241-
auto firstModule = firstDecl->getModuleContext();
242-
for (unsigned secondIdx = firstIdx + 1; secondIdx != n; ++secondIdx) {
243-
// Determine whether one module takes precedence over another.
244-
auto secondDecl = collidingDecls.second[secondIdx];
245-
auto secondModule = secondDecl->getModuleContext();
246-
247-
// If one declaration is in a protocol or extension thereof and the
248-
// other is not, prefer the one that is not.
249-
if ((bool)firstDecl->getDeclContext()
250-
->getAsProtocolOrProtocolExtensionContext()
251-
!= (bool)secondDecl->getDeclContext()
252-
->getAsProtocolOrProtocolExtensionContext()) {
253-
if (firstDecl->getDeclContext()
254-
->getAsProtocolOrProtocolExtensionContext()) {
255-
shadowed.insert(firstDecl);
256-
break;
257-
} else {
258-
shadowed.insert(secondDecl);
259-
continue;
260-
}
261-
}
262-
263-
// If one declaration is available and the other is not, prefer the
264-
// available one.
265-
if (firstDecl->getAttrs().isUnavailable(ctx) !=
266-
secondDecl->getAttrs().isUnavailable(ctx)) {
267-
if (firstDecl->getAttrs().isUnavailable(ctx)) {
268-
shadowed.insert(firstDecl);
269-
break;
270-
} else {
271-
shadowed.insert(secondDecl);
272-
continue;
273-
}
274-
}
275-
276-
// Don't apply module-shadowing rules to members of protocol types.
277-
if (isa<ProtocolDecl>(firstDecl->getDeclContext()) ||
278-
isa<ProtocolDecl>(secondDecl->getDeclContext()))
279-
continue;
280-
281-
// Prefer declarations in the current module over those in another
282-
// module.
283-
// FIXME: This is a hack. We should query a (lazily-built, cached)
284-
// module graph to determine shadowing.
285-
if ((firstModule == curModule) != (secondModule == curModule)) {
286-
// If the first module is the current module, the second declaration
287-
// is shadowed by the first.
288-
if (firstModule == curModule) {
289-
shadowed.insert(secondDecl);
290-
continue;
291-
}
292-
293-
// Otherwise, the first declaration is shadowed by the second. There is
294-
// no point in continuing to compare the first declaration to others.
295-
shadowed.insert(firstDecl);
296-
break;
297-
}
298-
299-
// Prefer declarations in an overlay to similar declarations in
300-
// the Clang module it customizes.
301-
if (firstDecl->hasClangNode() != secondDecl->hasClangNode()) {
302-
auto clangLoader = ctx.getClangModuleLoader();
303-
if (!clangLoader) continue;
304-
305-
if (clangLoader->isInOverlayModuleForImportedModule(
306-
firstDecl->getDeclContext(),
307-
secondDecl->getDeclContext())) {
308-
shadowed.insert(secondDecl);
309-
continue;
310-
}
311-
312-
if (clangLoader->isInOverlayModuleForImportedModule(
313-
secondDecl->getDeclContext(),
314-
firstDecl->getDeclContext())) {
315-
shadowed.insert(firstDecl);
316-
break;
317-
}
318-
}
319-
}
320-
}
321-
}
322-
323-
// Check for collisions among Objective-C initializers. When such collisions
324-
// exist, we pick the
325-
for (const auto &colliding : ObjCCollidingConstructors) {
326-
if (colliding.second.size() == 1)
378+
for (auto decl : decls) {
379+
auto known = collidingDeclGroups.find(decl->getFullName());
380+
if (known == collidingDeclGroups.end()) {
381+
// We already handled this group.
327382
continue;
328-
329-
// Find the "best" constructor with this signature.
330-
ConstructorDecl *bestCtor = colliding.second[0];
331-
for (auto ctor : colliding.second) {
332-
auto comparison = compareConstructors(ctor, bestCtor, ctx);
333-
if (comparison == ConstructorComparison::Better)
334-
bestCtor = ctor;
335383
}
336384

337-
// Shadow any initializers that are worse.
338-
for (auto ctor : colliding.second) {
339-
auto comparison = compareConstructors(ctor, bestCtor, ctx);
340-
if (comparison == ConstructorComparison::Worse)
341-
shadowed.insert(ctor);
342-
}
385+
recordShadowedDecls(known->second, curModule, shadowed);
386+
collidingDeclGroups.erase(known);
343387
}
344388

345-
// If none of the declarations were shadowed, we're done.
389+
// If no declarations were shadowed, we're done.
346390
if (shadowed.empty())
347391
return false;
348392

@@ -2277,8 +2321,7 @@ directReferencesForTypeRepr(Evaluator &evaluator,
22772321
}
22782322
}
22792323

2280-
static DirectlyReferencedTypeDecls
2281-
directReferencesForType(Type type) {
2324+
static DirectlyReferencedTypeDecls directReferencesForType(Type type) {
22822325
// If it's a typealias, return that.
22832326
if (auto aliasType = dyn_cast<NameAliasType>(type.getPointer()))
22842327
return { 1, aliasType->getDecl() };

lib/Sema/CSApply.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8085,6 +8085,7 @@ Solution::convertBooleanTypeToBuiltinI1(Expr *expr,
80858085
}
80868086

80878087
// The method is not generic, so there are no substitutions.
8088+
tc.validateDeclForNameLookup(builtinMethod);
80888089
auto builtinMethodType = builtinMethod->getInterfaceType()
80898090
->castTo<FunctionType>();
80908091

0 commit comments

Comments
 (0)