Skip to content

Commit cd7c802

Browse files
authored
Lookup using private discriminators should still find public things. (#4238)
Private and fileprivate declarations have a special "private discriminator" to keep them from colliding with declarations with the same signature in another file. The debugger uses this to prefer declarations in the file you're currently stopped in when running the expression parser. Unfortunately, the current logic didn't just prefer declarations in the current file---it /only/ took declarations in the current file, as long as there weren't zero. The fixed version will include any declarations in the current file plus any declarations with 'internal' or broader access. (Really we shouldn't do this at the lookup level at all; it should just be a tie-breaker in solution ranking in the constraint solver. But that would be a more intrusive change.) rdar://problem/27015195
1 parent 4ccf6c9 commit cd7c802

File tree

4 files changed

+63
-16
lines changed

4 files changed

+63
-16
lines changed

lib/AST/NameLookup.cpp

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -327,23 +327,34 @@ bool swift::removeShadowedDecls(SmallVectorImpl<ValueDecl*> &decls,
327327
return anyRemoved;
328328
}
329329

330-
static bool matchesDiscriminator(Identifier discriminator,
331-
const ValueDecl *value) {
330+
namespace {
331+
enum class DiscriminatorMatch {
332+
NoDiscriminator,
333+
Matches,
334+
Different
335+
};
336+
}
337+
338+
static DiscriminatorMatch matchDiscriminator(Identifier discriminator,
339+
const ValueDecl *value) {
332340
if (value->getFormalAccess() > Accessibility::FilePrivate)
333-
return false;
341+
return DiscriminatorMatch::NoDiscriminator;
334342

335343
auto containingFile =
336344
dyn_cast<FileUnit>(value->getDeclContext()->getModuleScopeContext());
337345
if (!containingFile)
338-
return false;
346+
return DiscriminatorMatch::Different;
339347

340-
return
341-
discriminator == containingFile->getDiscriminatorForPrivateValue(value);
348+
if (discriminator == containingFile->getDiscriminatorForPrivateValue(value))
349+
return DiscriminatorMatch::Matches;
350+
351+
return DiscriminatorMatch::Different;
342352
}
343353

344-
static bool matchesDiscriminator(Identifier discriminator,
345-
UnqualifiedLookupResult lookupResult) {
346-
return matchesDiscriminator(discriminator, lookupResult.getValueDecl());
354+
static DiscriminatorMatch
355+
matchDiscriminator(Identifier discriminator,
356+
UnqualifiedLookupResult lookupResult) {
357+
return matchDiscriminator(discriminator, lookupResult.getValueDecl());
347358
}
348359

349360
template <typename Result>
@@ -353,19 +364,21 @@ static void filterForDiscriminator(SmallVectorImpl<Result> &results,
353364
if (discriminator.empty())
354365
return;
355366

356-
auto doesNotMatch = [discriminator](Result next) -> bool {
357-
return !matchesDiscriminator(discriminator, next);
358-
};
359-
360-
auto lastMatchIter = std::find_if_not(results.rbegin(), results.rend(),
361-
doesNotMatch);
367+
auto lastMatchIter = std::find_if(results.rbegin(), results.rend(),
368+
[discriminator](Result next) -> bool {
369+
return
370+
matchDiscriminator(discriminator, next) == DiscriminatorMatch::Matches;
371+
});
362372
if (lastMatchIter == results.rend())
363373
return;
364374

365375
Result lastMatch = *lastMatchIter;
366376

367377
auto newEnd = std::remove_if(results.begin(), lastMatchIter.base()-1,
368-
doesNotMatch);
378+
[discriminator](Result next) -> bool {
379+
return
380+
matchDiscriminator(discriminator, next) == DiscriminatorMatch::Different;
381+
});
369382
results.erase(newEnd, results.end());
370383
results.push_back(lastMatch);
371384
}

test/NameBinding/Inputs/HasPrivateAccess1.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,10 @@ private var global: Int = 1
33
public struct MyStruct {
44
private static func method() -> Int? { return nil }
55
}
6+
7+
// Note: These are deliberately 'internal' here and 'private' in the other file.
8+
// They're testing that we don't filter out non-discriminated decls in lookup.
9+
internal func process(_ x: Int) -> Int { return x }
10+
extension MyStruct {
11+
internal static func process(_ x: Int) -> Int { return x }
12+
}

test/NameBinding/Inputs/HasPrivateAccess2.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,10 @@ private var global: String = "abc"
33
extension MyStruct {
44
private static func method() -> String? { return nil }
55
}
6+
7+
// Note: These are deliberately 'private' here and 'internal' in the other file.
8+
// They're testing that we don't filter out non-discriminated decls in lookup.
9+
private func process(_ x: String) -> String { return x }
10+
extension MyStruct {
11+
private static func process(_ x: String) -> String { return x }
12+
}

test/NameBinding/debug-client-discriminator.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,23 @@ let qualified = HasPrivateAccess.global
2121
// CHECK-INT: let result: Int?
2222
// CHECK-STRING: let result: String?
2323
let result = HasPrivateAccess.MyStruct.method()
24+
25+
// CHECK-ERROR: let processedInt: Int
26+
// CHECK-INT: let processedInt: Int
27+
// CHECK-STRING: let processedInt: Int
28+
let processedInt = process(1)
29+
30+
// CHECK-ERROR: let processedIntQualified: Int
31+
// CHECK-INT: let processedIntQualified: Int
32+
// CHECK-STRING: let processedIntQualified: Int
33+
let processedIntQualified = MyStruct.process(1)
34+
35+
// CHECK-ERROR: let processedString: String
36+
// CHECK-INT: let processedString: String
37+
// CHECK-STRING: let processedString: String
38+
let processedString = process("abc")
39+
40+
// CHECK-ERROR: let processedStringQualified: String
41+
// CHECK-INT: let processedStringQualified: String
42+
// CHECK-STRING: let processedStringQualified: String
43+
let processedStringQualified = MyStruct.process("abc")

0 commit comments

Comments
 (0)