Skip to content

Commit 51e4792

Browse files
committed
AST: Types nested inside a subclass shadow types in the superclass with same name
The existing rules sometimes behaved this way, because validateDecl() bails out silently without error on circularity. Let's make this explicit instead, and get it working in all cases. Fixes <rdar://https://bugs.swift.org/browse/SR-3492> and <rdar://problem/45889192>.
1 parent dd7d925 commit 51e4792

File tree

2 files changed

+43
-7
lines changed

2 files changed

+43
-7
lines changed

lib/AST/NameLookup.cpp

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ static void recordShadowedDeclsAfterSignatureMatch(
193193
for (unsigned firstIdx : indices(decls)) {
194194
auto firstDecl = decls[firstIdx];
195195
auto firstModule = firstDecl->getModuleContext();
196+
bool firstTopLevel = firstDecl->getDeclContext()->isModuleScopeContext();
197+
196198
auto name = firstDecl->getBaseName();
197199

198200
auto isShadowed = [&](ArrayRef<ModuleDecl::AccessPathTy> paths) {
@@ -219,15 +221,37 @@ static void recordShadowedDeclsAfterSignatureMatch(
219221
// Determine whether one module takes precedence over another.
220222
auto secondDecl = decls[secondIdx];
221223
auto secondModule = secondDecl->getModuleContext();
224+
bool secondTopLevel = secondDecl->getDeclContext()->isModuleScopeContext();
225+
226+
// For member types, we skip most of the below rules. Instead, we allow
227+
// member types defined in a subclass to shadow member types defined in
228+
// a superclass.
229+
if (isa<TypeDecl>(firstDecl) &&
230+
isa<TypeDecl>(secondDecl) &&
231+
!firstTopLevel &&
232+
!secondTopLevel) {
233+
auto *firstClass = firstDecl->getDeclContext()->getSelfClassDecl();
234+
auto *secondClass = secondDecl->getDeclContext()->getSelfClassDecl();
235+
if (firstClass && secondClass && firstClass != secondClass) {
236+
if (firstClass->isSuperclassOf(secondClass)) {
237+
shadowed.insert(firstDecl);
238+
continue;
239+
} else if (secondClass->isSuperclassOf(firstClass)) {
240+
shadowed.insert(secondDecl);
241+
continue;
242+
}
243+
}
244+
245+
continue;
246+
}
222247

223248
// Top-level type declarations in a module shadow other declarations
224249
// visible through the module's imports.
225250
//
226251
// [Backward compatibility] Note that members of types have the same
227252
// shadowing check, but we do it after dropping unavailable members.
228253
if (firstModule != secondModule &&
229-
firstDecl->getDeclContext()->isModuleScopeContext() &&
230-
secondDecl->getDeclContext()->isModuleScopeContext()) {
254+
firstTopLevel && secondTopLevel) {
231255
auto firstPaths = imports.getAllAccessPathsNotShadowedBy(
232256
firstModule, secondModule, dc);
233257
auto secondPaths = imports.getAllAccessPathsNotShadowedBy(
@@ -304,8 +328,7 @@ static void recordShadowedDeclsAfterSignatureMatch(
304328
// shadowing check is performed after unavailable candidates have
305329
// already been dropped.
306330
if (firstModule != secondModule &&
307-
!firstDecl->getDeclContext()->isModuleScopeContext() &&
308-
!secondDecl->getDeclContext()->isModuleScopeContext()) {
331+
!firstTopLevel && !secondTopLevel) {
309332
auto firstPaths = imports.getAllAccessPathsNotShadowedBy(
310333
firstModule, secondModule, dc);
311334
auto secondPaths = imports.getAllAccessPathsNotShadowedBy(
@@ -478,9 +501,6 @@ static void recordShadowedDecls(ArrayRef<ValueDecl *> decls,
478501
// type. This is layering a partial fix upon a total hack.
479502
if (auto asd = dyn_cast<AbstractStorageDecl>(decl))
480503
signature = asd->getOverloadSignatureType();
481-
} else if (decl->getDeclContext()->isTypeContext()) {
482-
// Do not apply shadowing rules for member types.
483-
continue;
484504
}
485505

486506
// Record this declaration based on its signature.

test/NameBinding/member_type_shadowing.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,20 @@ extension RootClass: P {
1212
typealias Member = SubClass.Member
1313
}
1414

15+
class A {
16+
enum Reusable {
17+
case option1
18+
case option2
19+
}
20+
}
21+
22+
class B: A {
23+
enum Reusable {
24+
case option1 // expected-note {{'option1' declared here}}
25+
}
1526

27+
func process() {
28+
_ = B.Reusable.option1
29+
_ = B.Reusable.option2 // expected-error {{type 'B.Reusable' has no member 'option2'; did you mean 'option1'?}}
30+
}
31+
}

0 commit comments

Comments
 (0)