Skip to content

Commit fa74c2f

Browse files
authored
Merge pull request swiftlang#27160 from slavapestov/shadowing-scoped-import-fix
AST: Fix source break with new shadowing rules
2 parents bd3b827 + 52479ca commit fa74c2f

File tree

4 files changed

+58
-49
lines changed

4 files changed

+58
-49
lines changed

include/swift/AST/ImportCache.h

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -140,52 +140,13 @@ class alignas(ModuleDecl::ImportedModule) ImportCache {
140140
return !getAllVisibleAccessPaths(mod, dc).empty();
141141
}
142142

143-
/// Determines if 'mod' is visible from 'dc' as a result of a scoped import.
144-
/// Note that if 'mod' was not imported from 'dc' at all, this also returns
145-
/// false.
146-
bool isScopedImport(const ModuleDecl *mod, DeclBaseName name,
147-
const DeclContext *dc) {
148-
auto accessPaths = getAllVisibleAccessPaths(mod, dc);
149-
for (auto accessPath : accessPaths) {
150-
if (accessPath.empty())
151-
continue;
152-
if (ModuleDecl::matchesAccessPath(accessPath, name))
153-
return true;
154-
}
155-
156-
return false;
157-
}
158-
159143
/// Returns all access paths in 'mod' that are visible from 'dc' if we
160144
/// subtract imports of 'other'.
161145
ArrayRef<ModuleDecl::AccessPathTy>
162146
getAllAccessPathsNotShadowedBy(const ModuleDecl *mod,
163147
const ModuleDecl *other,
164148
const DeclContext *dc);
165149

166-
/// Returns 'true' if a declaration named 'name' defined in 'other' shadows
167-
/// defined in 'mod', because no access paths can find 'name' in 'mod' from
168-
/// 'dc' if we ignore imports of 'other'.
169-
bool isShadowedBy(const ModuleDecl *mod,
170-
const ModuleDecl *other,
171-
DeclBaseName name,
172-
const DeclContext *dc) {
173-
auto accessPaths = getAllAccessPathsNotShadowedBy(mod, other, dc);
174-
return llvm::none_of(accessPaths,
175-
[&](ModuleDecl::AccessPathTy accessPath) {
176-
return ModuleDecl::matchesAccessPath(accessPath, name);
177-
});
178-
}
179-
180-
/// Qualified lookup into types uses a slightly different check that does not
181-
/// take access paths into account.
182-
bool isShadowedBy(const ModuleDecl *mod,
183-
const ModuleDecl *other,
184-
const DeclContext *dc) {
185-
auto accessPaths = getAllAccessPathsNotShadowedBy(mod, other, dc);
186-
return accessPaths.empty();
187-
}
188-
189150
/// This is a hack to cope with main file parsing and REPL parsing, where
190151
/// we can add ImportDecls after name binding.
191152
void clear() {

lib/AST/NameLookup.cpp

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,27 @@ static void recordShadowedDeclsAfterSignatureMatch(
193193
auto firstDecl = decls[firstIdx];
194194
auto firstModule = firstDecl->getModuleContext();
195195
auto name = firstDecl->getBaseName();
196+
197+
auto isShadowed = [&](ArrayRef<ModuleDecl::AccessPathTy> paths) {
198+
for (auto path : paths) {
199+
if (ModuleDecl::matchesAccessPath(path, name))
200+
return false;
201+
}
202+
203+
return true;
204+
};
205+
206+
auto isScopedImport = [&](ArrayRef<ModuleDecl::AccessPathTy> paths) {
207+
for (auto path : paths) {
208+
if (path.empty())
209+
continue;
210+
if (ModuleDecl::matchesAccessPath(path, name))
211+
return true;
212+
}
213+
214+
return false;
215+
};
216+
196217
for (unsigned secondIdx : range(firstIdx + 1, decls.size())) {
197218
// Determine whether one module takes precedence over another.
198219
auto secondDecl = decls[secondIdx];
@@ -206,22 +227,28 @@ static void recordShadowedDeclsAfterSignatureMatch(
206227
if (firstModule != secondModule &&
207228
firstDecl->getDeclContext()->isModuleScopeContext() &&
208229
secondDecl->getDeclContext()->isModuleScopeContext()) {
209-
// First, scoped imports shadow unscoped imports.
210-
bool firstScoped = imports.isScopedImport(firstModule, name, dc);
211-
bool secondScoped = imports.isScopedImport(secondModule, name, dc);
212-
if (!firstScoped && secondScoped) {
230+
auto firstPaths = imports.getAllAccessPathsNotShadowedBy(
231+
firstModule, secondModule, dc);
232+
auto secondPaths = imports.getAllAccessPathsNotShadowedBy(
233+
secondModule, firstModule, dc);
234+
235+
// Check if one module shadows the other.
236+
if (isShadowed(firstPaths)) {
213237
shadowed.insert(firstDecl);
214238
break;
215-
} else if (firstScoped && !secondScoped) {
239+
} else if (isShadowed(secondPaths)) {
216240
shadowed.insert(secondDecl);
217241
continue;
218242
}
219243

220-
// Now check if one module shadows the other.
221-
if (imports.isShadowedBy(firstModule, secondModule, name, dc)) {
244+
// We might be in a situation where neither module shadows the
245+
// other, but one declaration is visible via a scoped import.
246+
bool firstScoped = isScopedImport(firstPaths);
247+
bool secondScoped = isScopedImport(secondPaths);
248+
if (!firstScoped && secondScoped) {
222249
shadowed.insert(firstDecl);
223250
break;
224-
} else if (imports.isShadowedBy(secondModule, firstModule, name, dc)) {
251+
} else if (firstScoped && !secondScoped) {
225252
shadowed.insert(secondDecl);
226253
continue;
227254
}
@@ -278,10 +305,16 @@ static void recordShadowedDeclsAfterSignatureMatch(
278305
if (firstModule != secondModule &&
279306
!firstDecl->getDeclContext()->isModuleScopeContext() &&
280307
!secondDecl->getDeclContext()->isModuleScopeContext()) {
281-
if (imports.isShadowedBy(firstModule, secondModule, dc)) {
308+
auto firstPaths = imports.getAllAccessPathsNotShadowedBy(
309+
firstModule, secondModule, dc);
310+
auto secondPaths = imports.getAllAccessPathsNotShadowedBy(
311+
secondModule, firstModule, dc);
312+
313+
// Check if one module shadows the other.
314+
if (isShadowed(firstPaths)) {
282315
shadowed.insert(firstDecl);
283316
break;
284-
} else if (imports.isShadowedBy(secondModule, firstModule, dc)) {
317+
} else if (isShadowed(secondPaths)) {
285318
shadowed.insert(secondDecl);
286319
continue;
287320
}

test/NameBinding/Inputs/sdf.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@_exported import asdf
2+
3+
public struct S { public init(x: Int) {} }
4+
public struct D { public init(x: Int) {} }
5+
public struct F { public init(x: Int) {} }
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/asdf.swift
3+
// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/sdf.swift -I %t
4+
// RUN: %target-swift-frontend -typecheck %s -I %t -verify
5+
6+
import asdf
7+
import sdf
8+
import struct sdf.S
9+
10+
var uS: S = sdf.S(x: 123)

0 commit comments

Comments
 (0)