Skip to content

Commit 1c0ccf6

Browse files
authored
Merge pull request #68479 from xymus/access-level-fix-private
Sema: Improve diagnostics logic for references to imported decl behind a restrictive import
2 parents ebf46ea + 34ca1d5 commit 1c0ccf6

12 files changed

+281
-259
lines changed

include/swift/AST/Decl.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2788,6 +2788,16 @@ class ValueDecl : public Decl {
27882788
bool forConformance = false,
27892789
bool allowUsableFromInline = false) const;
27902790

2791+
using ImportAccessLevel = llvm::Optional<AttributedImport<ImportedModule>>;
2792+
2793+
/// Returns the import that may restrict the access to this decl
2794+
/// from \p useDC.
2795+
///
2796+
/// If this decl and \p useDC are from the same module it returns
2797+
/// \c llvm::None. If there are many imports, it returns the most
2798+
/// permissive one.
2799+
ImportAccessLevel getImportAccessFrom(const DeclContext *useDC) const;
2800+
27912801
/// Returns whether this declaration should be treated as \c open from
27922802
/// \p useDC. This is very similar to #getFormalAccess, but takes
27932803
/// \c \@testable into account.

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2332,12 +2332,12 @@ ERROR(access_level_conflict_with_exported,none,
23322332
(DeclAttribute, DeclAttribute))
23332333
NOTE(module_imported_here,none,
23342334
"module %0 imported as '%select{private|fileprivate|internal|package|%ERROR|%ERROR}1' here",
2335-
(Identifier, AccessLevel))
2335+
(const ModuleDecl*, AccessLevel))
23362336
NOTE(decl_import_via_here,none,
23372337
"%kind0 imported as "
23382338
"'%select{private|fileprivate|internal|package|%ERROR|%ERROR}1' "
23392339
"from %2 here",
2340-
(const ValueDecl *, AccessLevel, Identifier))
2340+
(const ValueDecl *, AccessLevel, const ModuleDecl*))
23412341

23422342
// Opaque return types
23432343
ERROR(opaque_type_invalid_constraint,none,

lib/AST/Decl.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4071,6 +4071,16 @@ getAccessScopeForFormalAccess(const ValueDecl *VD,
40714071
resultDC = resultDC->getParent();
40724072
}
40734073

4074+
auto localImportRestriction = VD->getImportAccessFrom(useDC);
4075+
if (localImportRestriction.has_value()) {
4076+
AccessLevel importAccessLevel =
4077+
localImportRestriction.value().accessLevel;
4078+
if (access > importAccessLevel) {
4079+
access = std::min(access, importAccessLevel);
4080+
resultDC = useDC->getParentSourceFile();
4081+
}
4082+
}
4083+
40744084
switch (access) {
40754085
case AccessLevel::Private:
40764086
case AccessLevel::FilePrivate:
@@ -4335,6 +4345,16 @@ bool ValueDecl::isAccessibleFrom(const DeclContext *useDC,
43354345
[&]() { return getFormalAccess(); });
43364346
}
43374347

4348+
ImportAccessLevel ValueDecl::getImportAccessFrom(const DeclContext *useDC) const {
4349+
ModuleDecl *Mod = getModuleContext();
4350+
if (useDC && useDC->getParentModule() != Mod) {
4351+
if (auto useSF = useDC->getParentSourceFile()) {
4352+
return useSF->getImportAccessLevel(Mod);
4353+
}
4354+
}
4355+
return llvm::None;
4356+
}
4357+
43384358
bool AbstractStorageDecl::isSetterAccessibleFrom(const DeclContext *DC,
43394359
bool forConformance) const {
43404360
assert(isSettable(DC));

lib/AST/Module.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3155,6 +3155,7 @@ bool SourceFile::hasTestableOrPrivateImport(
31553155
case AccessLevel::Internal:
31563156
case AccessLevel::Package:
31573157
case AccessLevel::Public:
3158+
case AccessLevel::Open:
31583159
// internal/public access only needs an import marked as @_private. The
31593160
// filename does not need to match (and we don't serialize it for such
31603161
// decls).
@@ -3173,8 +3174,6 @@ bool SourceFile::hasTestableOrPrivateImport(
31733174
desc.options.contains(ImportFlags::PrivateImport);
31743175
}
31753176
});
3176-
case AccessLevel::Open:
3177-
return true;
31783177
case AccessLevel::FilePrivate:
31793178
case AccessLevel::Private:
31803179
// Fallthrough.

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -61,28 +61,15 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
6161
return false;
6262

6363
// General check on access-level of the decl.
64+
auto *DC = where.getDeclContext();
6465
auto declAccessScope =
65-
D->getFormalAccessScope(/*useDC=*/nullptr,
66+
D->getFormalAccessScope(/*useDC=*/DC,
6667
/*allowUsableFromInline=*/true);
6768

68-
// If the decl is imported, check if the import lowers it's access level.
69-
auto importAccessLevel = AccessLevel::Public;
70-
ImportAccessLevel problematicImport = llvm::None;
71-
72-
auto *DC = where.getDeclContext();
73-
auto targetModule = D->getDeclContext()->getParentModule();
74-
auto file = where.getDeclContext()->getParentSourceFile();
75-
if (targetModule != DC->getParentModule() && file) {
76-
problematicImport = file->getImportAccessLevel(targetModule);
77-
if (problematicImport.has_value())
78-
importAccessLevel = problematicImport->accessLevel;
79-
}
80-
8169
// Public declarations are OK, even if they're SPI or came from an
8270
// implementation-only import. We'll diagnose exportability violations
8371
// from diagnoseDeclRefExportability().
84-
if (declAccessScope.isPublic() &&
85-
importAccessLevel == AccessLevel::Public)
72+
if (declAccessScope.isPublic())
8673
return false;
8774

8875
auto &Context = DC->getASTContext();
@@ -119,20 +106,19 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc,
119106
if (downgradeToWarning == DowngradeToWarning::Yes)
120107
diagID = diag::resilience_decl_unavailable_warn;
121108

122-
auto diagAccessLevel = std::min(declAccessScope.accessLevelForDiagnostics(),
123-
importAccessLevel);
124-
109+
AccessLevel diagAccessLevel = declAccessScope.accessLevelForDiagnostics();
125110
Context.Diags.diagnose(loc, diagID, D, diagAccessLevel,
126111
fragileKind.getSelector());
127112

128113
Context.Diags.diagnose(D, diag::resilience_decl_declared_here, D);
129114

115+
ImportAccessLevel problematicImport = D->getImportAccessFrom(DC);
130116
if (problematicImport.has_value() &&
131-
diagAccessLevel == importAccessLevel) {
117+
diagAccessLevel == problematicImport->accessLevel) {
132118
Context.Diags.diagnose(problematicImport->accessLevelLoc,
133119
diag::decl_import_via_here, D,
134120
problematicImport->accessLevel,
135-
problematicImport->module.importedModule->getName());
121+
problematicImport->module.importedModule);
136122
}
137123

138124
return (downgradeToWarning == DowngradeToWarning::No);

lib/Sema/TypeAccessScopeChecker.h

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ class TypeAccessScopeChecker {
3131
bool TreatUsableFromInlineAsPublic;
3232

3333
llvm::Optional<AccessScope> Scope = AccessScope::getPublic();
34-
ImportAccessLevel ImportRestriction = llvm::None;
3534

3635
TypeAccessScopeChecker(const DeclContext *useDC,
3736
bool treatUsableFromInlineAsPublic)
@@ -44,41 +43,22 @@ class TypeAccessScopeChecker {
4443

4544
auto AS = VD->getFormalAccessScope(File, TreatUsableFromInlineAsPublic);
4645
Scope = Scope->intersectWith(AS);
47-
48-
auto targetModule = VD->getDeclContext()->getParentModule();
49-
if (targetModule != File->getParentModule()) {
50-
auto localImportRestriction = File->getImportAccessLevel(targetModule);
51-
52-
if (localImportRestriction.has_value() &&
53-
(!ImportRestriction.has_value() ||
54-
localImportRestriction.value().accessLevel <
55-
ImportRestriction.value().accessLevel)) {
56-
ImportRestriction = localImportRestriction;
57-
}
58-
}
59-
6046
return Scope.has_value();
6147
}
6248

6349
public:
6450

65-
struct Result {
66-
llvm::Optional<AccessScope> Scope;
67-
ImportAccessLevel Import;
68-
};
69-
70-
static Result
51+
static llvm::Optional<AccessScope>
7152
getAccessScope(TypeRepr *TR, const DeclContext *useDC,
7253
bool treatUsableFromInlineAsPublic = false) {
7354
TypeAccessScopeChecker checker(useDC, treatUsableFromInlineAsPublic);
7455
TR->walk(TypeReprIdentFinder([&](const IdentTypeRepr *typeRepr) {
7556
return checker.visitDecl(typeRepr->getBoundDecl());
7657
}));
77-
return {checker.Scope,
78-
checker.ImportRestriction};
58+
return checker.Scope;
7959
}
8060

81-
static Result
61+
static llvm::Optional<AccessScope>
8262
getAccessScope(Type T, const DeclContext *useDC,
8363
bool treatUsableFromInlineAsPublic = false) {
8464
TypeAccessScopeChecker checker(useDC, treatUsableFromInlineAsPublic);
@@ -88,8 +68,7 @@ class TypeAccessScopeChecker {
8868
return TypeWalker::Action::Stop;
8969
}));
9070

91-
return {checker.Scope,
92-
checker.ImportRestriction};
71+
return checker.Scope;
9372
}
9473
};
9574

0 commit comments

Comments
 (0)