Skip to content

Commit c0a57b6

Browse files
authored
Merge pull request #27365 from slavapestov/member-type-shadowing-rule
AST: Types nested inside a subclass shadow types in the superclass with same name
2 parents 10b1bae + 44827bb commit c0a57b6

File tree

6 files changed

+182
-129
lines changed

6 files changed

+182
-129
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.

lib/Sema/LookupVisibleDecls.cpp

Lines changed: 103 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -708,9 +708,10 @@ namespace {
708708

709709
class OverrideFilteringConsumer : public VisibleDeclConsumer {
710710
public:
711-
std::set<ValueDecl *> AllFoundDecls;
712-
std::map<DeclBaseName, std::set<ValueDecl *>> FoundDecls;
713-
llvm::SetVector<FoundDeclTy> DeclsToReport;
711+
llvm::SetVector<FoundDeclTy> Results;
712+
llvm::SmallVector<ValueDecl *, 8> Decls;
713+
llvm::SetVector<FoundDeclTy> FilteredResults;
714+
llvm::DenseMap<DeclBaseName, llvm::SmallVector<ValueDecl *, 2>> DeclsByName;
714715
Type BaseTy;
715716
const DeclContext *DC;
716717

@@ -723,133 +724,128 @@ class OverrideFilteringConsumer : public VisibleDeclConsumer {
723724

724725
void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason,
725726
DynamicLookupInfo dynamicLookupInfo) override {
726-
if (!AllFoundDecls.insert(VD).second)
727+
if (!Results.insert({VD, Reason, dynamicLookupInfo}))
727728
return;
728729

729-
// If this kind of declaration doesn't participate in overriding, there's
730-
// no filtering to do here.
731-
if (!isa<AbstractFunctionDecl>(VD) &&
732-
!isa<AbstractStorageDecl>(VD) &&
733-
!isa<AssociatedTypeDecl>(VD)) {
734-
DeclsToReport.insert(FoundDeclTy(VD, Reason, dynamicLookupInfo));
735-
return;
736-
}
730+
DeclsByName[VD->getBaseName()] = {};
731+
Decls.push_back(VD);
732+
}
737733

738-
if (!VD->getInterfaceType()) {
739-
return;
740-
}
734+
void filterDecls(VisibleDeclConsumer &Consumer) {
735+
removeOverriddenDecls(Decls);
736+
removeShadowedDecls(Decls, DC);
741737

742-
if (VD->isInvalid()) {
743-
FoundDecls[VD->getBaseName()].insert(VD);
744-
DeclsToReport.insert(FoundDeclTy(VD, Reason, dynamicLookupInfo));
745-
return;
746-
}
747-
auto &PossiblyConflicting = FoundDecls[VD->getBaseName()];
738+
size_t index = 0;
739+
for (auto DeclAndReason : Results) {
740+
if (index >= Decls.size())
741+
break;
742+
if (DeclAndReason.D != Decls[index])
743+
continue;
748744

749-
// Check all overridden decls.
750-
{
751-
auto *CurrentVD = VD->getOverriddenDecl();
752-
while (CurrentVD) {
753-
if (!AllFoundDecls.insert(CurrentVD).second)
754-
break;
755-
if (PossiblyConflicting.count(CurrentVD)) {
756-
PossiblyConflicting.erase(CurrentVD);
757-
PossiblyConflicting.insert(VD);
745+
index++;
758746

759-
bool Erased = DeclsToReport.remove(
760-
FoundDeclTy(CurrentVD, DeclVisibilityKind::LocalVariable, {}));
761-
assert(Erased);
762-
(void)Erased;
747+
auto *VD = DeclAndReason.D;
748+
auto Reason = DeclAndReason.Reason;
749+
auto dynamicLookupInfo = DeclAndReason.dynamicLookupInfo;
763750

764-
DeclsToReport.insert(FoundDeclTy(VD, Reason, dynamicLookupInfo));
765-
return;
766-
}
767-
CurrentVD = CurrentVD->getOverriddenDecl();
751+
// If this kind of declaration doesn't participate in overriding, there's
752+
// no filtering to do here.
753+
if (!isa<AbstractFunctionDecl>(VD) &&
754+
!isa<AbstractStorageDecl>(VD) &&
755+
!isa<AssociatedTypeDecl>(VD)) {
756+
FilteredResults.insert(FoundDeclTy(VD, Reason, dynamicLookupInfo));
757+
continue;
768758
}
769-
}
770759

771-
// Does it make sense to substitute types?
772-
773-
// If the base type is AnyObject, we might be doing a dynamic
774-
// lookup, so the base type won't match the type of the member's
775-
// context type.
776-
//
777-
// If the base type is not a nominal type, we can't substitute
778-
// the member type.
779-
//
780-
// If the member is a free function and not a member of a type,
781-
// don't substitute either.
782-
bool shouldSubst = (Reason != DeclVisibilityKind::DynamicLookup &&
783-
!BaseTy->isAnyObject() && !BaseTy->hasTypeVariable() &&
784-
!BaseTy->hasUnboundGenericType() &&
785-
(BaseTy->getNominalOrBoundGenericNominal() ||
786-
BaseTy->is<ArchetypeType>()) &&
787-
VD->getDeclContext()->isTypeContext());
788-
ModuleDecl *M = DC->getParentModule();
789-
790-
// Hack; we shouldn't be filtering at this level anyway.
791-
if (!VD->hasInterfaceType()) {
792-
FoundDecls[VD->getBaseName()].insert(VD);
793-
DeclsToReport.insert(FoundDeclTy(VD, Reason, dynamicLookupInfo));
794-
return;
795-
}
760+
if (!VD->getInterfaceType()) {
761+
continue;
762+
}
796763

797-
auto FoundSignature = VD->getOverloadSignature();
798-
auto FoundSignatureType = VD->getOverloadSignatureType();
799-
if (FoundSignatureType && shouldSubst) {
800-
auto subs = BaseTy->getMemberSubstitutionMap(M, VD);
801-
auto CT = FoundSignatureType.subst(subs);
802-
if (!CT->hasError())
803-
FoundSignatureType = CT->getCanonicalType();
804-
}
764+
auto &PossiblyConflicting = DeclsByName[VD->getBaseName()];
805765

806-
for (auto I = PossiblyConflicting.begin(), E = PossiblyConflicting.end();
807-
I != E; ++I) {
808-
auto *OtherVD = *I;
809-
if (OtherVD->isInvalid() || !OtherVD->hasInterfaceType()) {
810-
// For some invalid decls it might be impossible to compute the
811-
// signature, for example, if the types could not be resolved.
766+
if (VD->isInvalid()) {
767+
FilteredResults.insert(FoundDeclTy(VD, Reason, dynamicLookupInfo));
768+
PossiblyConflicting.push_back(VD);
812769
continue;
813770
}
814771

815-
auto OtherSignature = OtherVD->getOverloadSignature();
816-
auto OtherSignatureType = OtherVD->getOverloadSignatureType();
817-
if (OtherSignatureType && shouldSubst) {
818-
auto ActualBaseTy = getBaseTypeForMember(M, OtherVD, BaseTy);
819-
auto subs = ActualBaseTy->getMemberSubstitutionMap(M, OtherVD);
820-
auto CT = OtherSignatureType.subst(subs);
772+
// Does it make sense to substitute types?
773+
774+
// If the base type is AnyObject, we might be doing a dynamic
775+
// lookup, so the base type won't match the type of the member's
776+
// context type.
777+
//
778+
// If the base type is not a nominal type, we can't substitute
779+
// the member type.
780+
//
781+
// If the member is a free function and not a member of a type,
782+
// don't substitute either.
783+
bool shouldSubst = (Reason != DeclVisibilityKind::DynamicLookup &&
784+
!BaseTy->isAnyObject() && !BaseTy->hasTypeVariable() &&
785+
!BaseTy->hasUnboundGenericType() &&
786+
(BaseTy->getNominalOrBoundGenericNominal() ||
787+
BaseTy->is<ArchetypeType>()) &&
788+
VD->getDeclContext()->isTypeContext());
789+
ModuleDecl *M = DC->getParentModule();
790+
791+
auto FoundSignature = VD->getOverloadSignature();
792+
auto FoundSignatureType = VD->getOverloadSignatureType();
793+
if (FoundSignatureType && shouldSubst) {
794+
auto subs = BaseTy->getMemberSubstitutionMap(M, VD);
795+
auto CT = FoundSignatureType.subst(subs);
821796
if (!CT->hasError())
822-
OtherSignatureType = CT->getCanonicalType();
797+
FoundSignatureType = CT->getCanonicalType();
823798
}
824799

825-
if (conflicting(M->getASTContext(), FoundSignature, FoundSignatureType,
826-
OtherSignature, OtherSignatureType,
827-
/*wouldConflictInSwift5*/nullptr,
828-
/*skipProtocolExtensionCheck*/true)) {
829-
if (VD->getFormalAccess() > OtherVD->getFormalAccess() ||
830-
//Prefer available one.
831-
(!AvailableAttr::isUnavailable(VD) &&
832-
AvailableAttr::isUnavailable(OtherVD))) {
833-
PossiblyConflicting.erase(I);
834-
PossiblyConflicting.insert(VD);
835-
836-
bool Erased = DeclsToReport.remove(
837-
FoundDeclTy(OtherVD, DeclVisibilityKind::LocalVariable, {}));
838-
assert(Erased);
839-
(void)Erased;
840-
841-
DeclsToReport.insert(FoundDeclTy(VD, Reason, dynamicLookupInfo));
800+
bool FoundConflicting = false;
801+
for (auto I = PossiblyConflicting.begin(), E = PossiblyConflicting.end();
802+
I != E; ++I) {
803+
auto *OtherVD = *I;
804+
if (OtherVD->isInvalid() || !OtherVD->getInterfaceType()) {
805+
// For some invalid decls it might be impossible to compute the
806+
// signature, for example, if the types could not be resolved.
807+
continue;
842808
}
843-
return;
809+
810+
auto OtherSignature = OtherVD->getOverloadSignature();
811+
auto OtherSignatureType = OtherVD->getOverloadSignatureType();
812+
if (OtherSignatureType && shouldSubst) {
813+
auto ActualBaseTy = getBaseTypeForMember(M, OtherVD, BaseTy);
814+
auto subs = ActualBaseTy->getMemberSubstitutionMap(M, OtherVD);
815+
auto CT = OtherSignatureType.subst(subs);
816+
if (!CT->hasError())
817+
OtherSignatureType = CT->getCanonicalType();
818+
}
819+
820+
if (conflicting(M->getASTContext(), FoundSignature, FoundSignatureType,
821+
OtherSignature, OtherSignatureType,
822+
/*wouldConflictInSwift5*/nullptr,
823+
/*skipProtocolExtensionCheck*/true)) {
824+
FoundConflicting = true;
825+
if (VD->getFormalAccess() > OtherVD->getFormalAccess() ||
826+
//Prefer available one.
827+
(!AvailableAttr::isUnavailable(VD) &&
828+
AvailableAttr::isUnavailable(OtherVD))) {
829+
FilteredResults.remove(
830+
FoundDeclTy(OtherVD, DeclVisibilityKind::LocalVariable, {}));
831+
FilteredResults.insert(FoundDeclTy(VD, Reason, dynamicLookupInfo));
832+
*I = VD;
833+
}
834+
}
835+
}
836+
837+
if (!FoundConflicting) {
838+
FilteredResults.insert(FoundDeclTy(VD, Reason, dynamicLookupInfo));
839+
PossiblyConflicting.push_back(VD);
844840
}
845841
}
846842

847-
PossiblyConflicting.insert(VD);
848-
DeclsToReport.insert(FoundDeclTy(VD, Reason, dynamicLookupInfo));
843+
for (auto Result : FilteredResults)
844+
Consumer.foundDecl(Result.D, Result.Reason, Result.dynamicLookupInfo);
849845
}
850846

851847
bool seenBaseName(DeclBaseName name) {
852-
return FoundDecls.find(name) != FoundDecls.end();
848+
return DeclsByName.find(name) != DeclsByName.end();
853849
}
854850
};
855851

@@ -1003,9 +999,7 @@ static void lookupVisibleMemberDecls(
1003999
GSB, Visited, seenDynamicLookup);
10041000

10051001
// Report the declarations we found to the real consumer.
1006-
for (const auto &DeclAndReason : overrideConsumer.DeclsToReport)
1007-
Consumer.foundDecl(DeclAndReason.D, DeclAndReason.Reason,
1008-
DeclAndReason.dynamicLookupInfo);
1002+
overrideConsumer.filterDecls(Consumer);
10091003
}
10101004

10111005
static void lookupVisibleDeclsImpl(VisibleDeclConsumer &Consumer,

test/IDE/complete_crashes.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,8 +355,8 @@ extension Foo {
355355
}
356356
#endif
357357
// RDAR_41234606: Begin completion
358-
// RDAR_41234606-DAG: Decl[AssociatedType]/Super: .Element; name=Element
359-
// RDAR_41234606-DAG: Decl[AssociatedType]/Super: .Iterator; name=Iterator
358+
// RDAR_41234606-DAG: Decl[AssociatedType]/CurrNominal: .Element; name=Element
359+
// RDAR_41234606-DAG: Decl[AssociatedType]/CurrNominal: .Iterator; name=Iterator
360360
// RDAR_41234606: End completions
361361

362362
// rdar://problem/41071587

test/IDE/complete_member_type.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=A | %FileCheck %s
2+
3+
class A {
4+
typealias T = Int
5+
enum E {
6+
case a
7+
case b
8+
}
9+
}
10+
11+
class B : A {
12+
typealias T = String
13+
struct E {}
14+
}
15+
16+
func foo() {
17+
_ = B.#^A^#
18+
}
19+
20+
// CHECK-LABEL: Begin completions, 5 items
21+
// CHECK-NEXT: Keyword[self]/CurrNominal: self[#B.Type#]; name=self
22+
// CHECK-NEXT: Keyword/CurrNominal: Type[#B.Type#]; name=Type
23+
// CHECK-NEXT: Decl[TypeAlias]/CurrNominal: T[#String#]; name=T
24+
// CHECK-NEXT: Decl[Struct]/CurrNominal: E[#B.E#]; name=E
25+
// CHECK-NEXT: Decl[Constructor]/CurrNominal: init()[#B#]; name=init()
26+
// CHECK-NEXT: End completions

test/IDE/complete_value_expr.swift

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,22 +1672,19 @@ func checkOverrideInclusion2(_ arg: Override3) {
16721672
// CHECK_NODUP_RESTATED_REQ-NOT: Decl[InstanceVar]/{{Super|CurrNominal}}: doo[#Int#]; name=doo
16731673
// CHECK_NODUP_RESTATED_REQ: End completions
16741674
1675-
// CHECK_NODUP_RESTATED_REQ_NODOT: Begin completions
1676-
// CHECK_NODUP_RESTATED_REQ_NODOT: Decl[InstanceMethod]/{{Super|CurrNominal}}: .foo()[#Void#]; name=foo()
1677-
// CHECK_NODUP_RESTATED_REQ_NODOT: Decl[InstanceMethod]/{{Super|CurrNominal}}: .roo({#arg1: Int#})[#Void#];
1678-
// CHECK_NODUP_RESTATED_REQ_NODOT: Decl[Subscript]/{{Super|CurrNominal}}: [{#(arg): Bool#}][#Bool#]; name=[arg: Bool]
1679-
// CHECK_NODUP_RESTATED_REQ_NODOT: Decl[InstanceVar]/{{Super|CurrNominal}}: .doo[#Int#]; name=doo
1680-
// CHECK_NODUP_RESTATED_REQ_NODOT: Decl[InstanceMethod]/{{Super|CurrNominal}}: .roo({#arg2: Int#})[#Void#];
1681-
// CHECK_NODUP_RESTATED_REQ_NODOT-NOT: Decl[InstanceMethod]/{{Super|CurrNominal}}: .foo()[#Void#]; name=foo()
1682-
// CHECK_NODUP_RESTATED_REQ_NODOT-NOT: Decl[Subscript]/{{Super|CurrNominal}}: [{#Bool#}][#Bool#]; name=[Bool]
1683-
// CHECK_NODUP_RESTATED_REQ_NODOT-NOT: Decl[InstanceVar]/{{Super|CurrNominal}}: .doo[#Int#]; name=doo
1675+
// CHECK_NODUP_RESTATED_REQ_NODOT: Begin completions, 6 items
1676+
// CHECK_NODUP_RESTATED_REQ_NODOT-DAG: Decl[InstanceMethod]/{{Super|CurrNominal}}: .foo()[#Void#]; name=foo()
1677+
// CHECK_NODUP_RESTATED_REQ_NODOT-DAG: Decl[InstanceMethod]/{{Super|CurrNominal}}: .roo({#arg1: Int#})[#Void#];
1678+
// CHECK_NODUP_RESTATED_REQ_NODOT-DAG: Decl[Subscript]/{{Super|CurrNominal}}: [{#(arg): Bool#}][#Bool#]; name=[arg: Bool]
1679+
// CHECK_NODUP_RESTATED_REQ_NODOT-DAG: Decl[InstanceVar]/{{Super|CurrNominal}}: .doo[#Int#]; name=doo
1680+
// CHECK_NODUP_RESTATED_REQ_NODOT-DAG: Decl[InstanceMethod]/{{Super|CurrNominal}}: .roo({#arg2: Int#})[#Void#];
16841681
// CHECK_NODUP_RESTATED_REQ_NODOT: End completions
16851682
16861683
// CHECK_NODUP_RESTATED_REQ_TYPE1: Begin completions, 6 items
1687-
// CHECK_NODUP_RESTATED_REQ_TYPE1: Decl[InstanceMethod]/Super: foo({#(self): NoDupReq6#})[#() -> Void#]; name=foo(self: NoDupReq6)
16881684
// CHECK_NODUP_RESTATED_REQ_TYPE1: Decl[InstanceMethod]/Super: roo({#(self): NoDupReq6#})[#(arg1: Int) -> Void#]; name=roo(self: NoDupReq6)
1689-
// CHECK_NODUP_RESTATED_REQ_TYPE1: Decl[AssociatedType]/Super: E; name=E
1685+
// CHECK_NODUP_RESTATED_REQ_TYPE1: Decl[InstanceMethod]/CurrNominal: foo({#(self): NoDupReq6#})[#() -> Void#]; name=foo(self: NoDupReq6)
16901686
// CHECK_NODUP_RESTATED_REQ_TYPE1: Decl[InstanceMethod]/CurrNominal: roo({#(self): NoDupReq6#})[#(arg2: Int) -> Void#]; name=roo(self: NoDupReq6)
1687+
// CHECK_NODUP_RESTATED_REQ_TYPE1: Decl[AssociatedType]/CurrNominal: E; name=E
16911688
// CHECK_NODUP_RESTATED_REQ_TYPE1: End completions
16921689
16931690
// CHECK_NODUP_RESTATED_REQ_TYPE2: Begin completions, 6 items

0 commit comments

Comments
 (0)