Skip to content

Commit 248ca75

Browse files
committed
[Type checking] A declaration in an overlay can shadow an imported declaration.
If a declaration provided in a Swift overlay has the same signature as a declaration imported from the Clang module it overlays, or some private framework that re-exports its interface through that Clang module, the declaration provided in the Swift overlay shadows the one imported from Clang. To make this work for unqualified lookup, allow the shadowing rules to kick in there as well. Make this a very narrow fix, because the underlying problems that prevented us from doing this shadowing still persist. Fixes rdar://problem/35691030 and its several dupes.
1 parent db75a67 commit 248ca75

File tree

11 files changed

+115
-28
lines changed

11 files changed

+115
-28
lines changed

lib/AST/NameLookup.cpp

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
#include "NameLookupImpl.h"
18-
#include "swift/Basic/Statistic.h"
18+
#include "swift/ClangImporter/ClangImporter.h"
1919
#include "swift/AST/NameLookup.h"
2020
#include "swift/AST/ASTContext.h"
2121
#include "swift/AST/ASTScope.h"
@@ -26,6 +26,7 @@
2626
#include "swift/AST/Initializer.h"
2727
#include "swift/AST/ReferencedNameTracker.h"
2828
#include "swift/Basic/SourceManager.h"
29+
#include "swift/Basic/Statistic.h"
2930
#include "swift/Basic/STLExtras.h"
3031
#include "llvm/ADT/DenseMap.h"
3132
#include "llvm/ADT/TinyPtrVector.h"
@@ -281,20 +282,35 @@ bool swift::removeShadowedDecls(SmallVectorImpl<ValueDecl*> &decls,
281282
// module.
282283
// FIXME: This is a hack. We should query a (lazily-built, cached)
283284
// module graph to determine shadowing.
284-
if ((firstModule == curModule) == (secondModule == curModule))
285-
continue;
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+
}
286292

287-
// If the first module is the current module, the second declaration
288-
// is shadowed by the first.
289-
if (firstModule == curModule) {
290-
shadowed.insert(secondDecl);
291-
continue;
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;
292297
}
293298

294-
// Otherwise, the first declaration is shadowed by the second. There is
295-
// no point in continuing to compare the first declaration to others.
296-
shadowed.insert(firstDecl);
297-
break;
299+
// Prefer declarations in an overlay to similar declarations in
300+
// the Clang module it customizes.
301+
if (firstDecl->hasClangNode() != secondDecl->hasClangNode()) {
302+
if (isInOverlayModuleForImportedModule(firstDecl->getDeclContext(),
303+
secondDecl->getDeclContext())){
304+
shadowed.insert(secondDecl);
305+
continue;
306+
}
307+
308+
if (isInOverlayModuleForImportedModule(secondDecl->getDeclContext(),
309+
firstDecl->getDeclContext())) {
310+
shadowed.insert(firstDecl);
311+
break;
312+
}
313+
}
298314
}
299315
}
300316
}

lib/Sema/TypeCheckNameLookup.cpp

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,37 @@ namespace {
7575
Options |= NameLookupFlags::IgnoreAccessControl;
7676
}
7777

78+
/// Determine whether we should filter out the results by removing
79+
/// overridden and shadowed declarations.
80+
/// FIXME: We should *always* do this, but there are weird assumptions
81+
/// about the results of unqualified name lookup, e.g., that a local
82+
/// variable not having a type indicates that it hasn't been seen yet.
83+
bool shouldFilterResults() const {
84+
// Member lookups always filter results.
85+
if (IsMemberLookup) return true;
86+
87+
bool allAreInOtherModules = true;
88+
auto currentModule = DC->getParentModule();
89+
for (const auto &found : Result) {
90+
// We found a member, so we need to filter.
91+
if (found.getBaseDecl() != nullptr)
92+
return true;
93+
94+
// We found something in our own module.
95+
if (found.getValueDecl()->getDeclContext()->getParentModule() ==
96+
currentModule)
97+
allAreInOtherModules = false;
98+
}
99+
100+
// FIXME: Only perform shadowing if we found things from other modules.
101+
// This prevents us from introducing additional type-checking work
102+
// during name lookup.
103+
return allAreInOtherModules;
104+
}
105+
78106
~LookupResultBuilder() {
79-
// If any of the results have a base, we need to remove
80-
// overridden and shadowed declarations.
81-
// FIXME: We should *always* remove overridden and shadowed declarations,
82-
// but there are weird assumptions about the results of unqualified
83-
// name lookup, e.g., that a local variable not having a type indicates
84-
// that it hasn't been seen yet.
85-
if (!IsMemberLookup &&
86-
std::find_if(Result.begin(), Result.end(),
87-
[](const LookupResultEntry &found) {
88-
return found.getBaseDecl() != nullptr;
89-
}) == Result.end())
90-
return;
107+
// Check whether we should do this filtering aat all.
108+
if (!shouldFilterResults()) return;
91109

92110
// Remove any overridden declarations from the found-declarations set.
93111
removeOverriddenDecls(FoundDecls);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#import <SomeKit/SKWidget.h>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#import <SomeKitCore/SKWidget.h>

test/ClangImporter/Inputs/privateframeworks/overlay/SomeKit.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,10 @@ public func inlineWidgetOperations(_ widget: SKWidget) {
4747
someKitGlobalFunc()
4848
hao.anObject = widget
4949
}
50+
51+
@available(swift, deprecated: 4.2)
52+
public func someKitOtherGlobalFunc() { }
53+
54+
extension SKWidget {
55+
public var name: String { return "blah" }
56+
}

test/ClangImporter/Inputs/privateframeworks/withoutprivate/SomeKit.framework/Headers/SKWidget.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,8 @@ typedef enum __attribute__((ns_error_domain(SKWidgetErrorDomain))) __attribute__
2424
@end
2525

2626
extern void someKitGlobalFunc(void);
27+
28+
static inline void someKitOtherGlobalFunc(void) { }
29+
30+
extern NSString * _Nonnull someKitGetWidgetName(SKWidget * _Nonnull)
31+
__attribute__((swift_name("getter:SKWidget.name(self:)")));
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
Name: SomeKit
3+
Functions:
4+
- Name: someKitOtherGlobalFunc
5+
SwiftPrivate: true
6+
SwiftVersions:
7+
- Version: 4
8+
Functions:
9+
- Name: someKitOtherGlobalFunc
10+
SwiftPrivate: false
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
Name: SomeKit
3+
SwiftVersions:
4+
- Version: 4
5+
Functions:
6+
- Name: someKitOtherGlobalFunc
7+
SwiftPrivate: false

test/ClangImporter/Inputs/privateframeworks/withprivate/SomeKitCore.framework/Headers/SKWidget.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,8 @@ typedef enum __attribute__((ns_error_domain(SKWidgetErrorDomain))) __attribute__
2525
@end
2626

2727
extern void someKitGlobalFunc(void);
28+
29+
static inline void someKitOtherGlobalFunc(void) { }
30+
31+
extern NSString * _Nonnull someKitGetWidgetName(SKWidget * _Nonnull)
32+
__attribute__((swift_name("getter:SKWidget.name(self:)")));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
Name: SomeKitCore
3+
Functions:
4+
- Name: someKitOtherGlobalFunc
5+
SwiftPrivate: true
6+
SwiftVersions:
7+
- Version: 4
8+
Functions:
9+
- Name: someKitOtherGlobalFunc
10+
SwiftPrivate: false

test/ClangImporter/private_frameworks.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@
99
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-module -F %S/Inputs/privateframeworks/withprivate -o %t %S/Inputs/privateframeworks/overlay/SomeKit.swift
1010

1111
// Use the overlay with private frameworks.
12-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-sil -o /dev/null -F %S/Inputs/privateframeworks/withprivate %s -verify
12+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-sil -o /dev/null -F %S/Inputs/privateframeworks/withprivate -swift-version 4 %s -import-objc-header %S/Inputs/privateframeworks/bridging-somekitcore.h -verify
1313

1414
// Use the overlay without private frameworks.
15-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-sil -o /dev/null -F %S/Inputs/privateframeworks/withoutprivate -I %t %s
15+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-sil -o /dev/null -F %S/Inputs/privateframeworks/withoutprivate -I %t -swift-version 4 -import-objc-header %S/Inputs/privateframeworks/bridging-somekit.h %s
1616

1717
// Build the overlay with public frameworks.
1818
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-module -F %S/Inputs/privateframeworks/withoutprivate -o %t %S/Inputs/privateframeworks/overlay/SomeKit.swift
1919

2020
// Use the overlay with private frameworks.
21-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-sil -o /dev/null -F %S/Inputs/privateframeworks/withprivate %s -verify
21+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-sil -o /dev/null -F %S/Inputs/privateframeworks/withprivate -swift-version 4 %s -import-objc-header %S/Inputs/privateframeworks/bridging-somekitcore.h -verify
2222

2323
// Use the overlay without private frameworks.
24-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-sil -o /dev/null -F %S/Inputs/privateframeworks/withoutprivate -I %t %s
24+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-sil -o /dev/null -F %S/Inputs/privateframeworks/withoutprivate -I %t -swift-version 4 %s -import-objc-header %S/Inputs/privateframeworks/bridging-somekit.h
2525

2626
// REQUIRES: objc_interop
2727

@@ -36,10 +36,17 @@ func testWidget(widget: SKWidget) {
3636

3737
widget.doSomethingElse(widget)
3838
inlineWidgetOperations(widget)
39+
40+
let _ = widget.name
3941
}
4042

4143
func testError(widget: SKWidget) {
4244
let c: SKWidget.Error.Code = SKWidget.Error(.boom).getCode(from: widget)
4345
if c.isBoom { }
4446
}
4547

48+
func testGlobals() {
49+
someKitGlobalFunc()
50+
SomeKit.someKitOtherGlobalFunc()
51+
someKitOtherGlobalFunc()
52+
}

0 commit comments

Comments
 (0)