Skip to content

Commit 1bf3686

Browse files
authored
Merge pull request #68776 from xymus/access-level-import-indirect-checks
Sema: report conformances and typealias underlying types behind non-public imports
2 parents d047419 + 8f195b0 commit 1bf3686

File tree

8 files changed

+129
-21
lines changed

8 files changed

+129
-21
lines changed

include/swift/AST/Decl.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,16 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
982982
/// Returns the source range of the declaration including its attributes.
983983
SourceRange getSourceRangeIncludingAttrs() const;
984984

985+
using ImportAccessLevel = llvm::Optional<AttributedImport<ImportedModule>>;
986+
987+
/// Returns the import that may restrict the access to this decl
988+
/// from \p useDC.
989+
///
990+
/// If this decl and \p useDC are from the same module it returns
991+
/// \c llvm::None. If there are many imports, it returns the most
992+
/// permissive one.
993+
ImportAccessLevel getImportAccessFrom(const DeclContext *useDC) const;
994+
985995
SourceLoc TrailingSemiLoc;
986996

987997
/// Whether this declaration is within a macro expansion relative to
@@ -2793,15 +2803,6 @@ class ValueDecl : public Decl {
27932803
bool forConformance = false,
27942804
bool allowUsableFromInline = false) const;
27952805

2796-
using ImportAccessLevel = llvm::Optional<AttributedImport<ImportedModule>>;
2797-
2798-
/// Returns the import that may restrict the access to this decl
2799-
/// from \p useDC.
2800-
///
2801-
/// If this decl and \p useDC are from the same module it returns
2802-
/// \c llvm::None. If there are many imports, it returns the most
2803-
/// permissive one.
2804-
ImportAccessLevel getImportAccessFrom(const DeclContext *useDC) const;
28052806

28062807
/// Returns whether this declaration should be treated as \c open from
28072808
/// \p useDC. This is very similar to #getFormalAccess, but takes

include/swift/AST/DiagnosticsSema.def

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2334,7 +2334,7 @@ NOTE(decl_import_via_here,none,
23342334
"%kind0 imported as "
23352335
"'%select{private|fileprivate|internal|package|%ERROR|%ERROR}1' "
23362336
"from %2 here",
2337-
(const ValueDecl *, AccessLevel, const ModuleDecl*))
2337+
(const Decl *, AccessLevel, const ModuleDecl*))
23382338

23392339
// Opaque return types
23402340
ERROR(opaque_type_invalid_constraint,none,
@@ -3437,7 +3437,8 @@ ERROR(decl_from_hidden_module,none,
34373437
"it is SPI|"
34383438
"%2 was imported for SPI only|"
34393439
"%2 was not imported by this file|"
3440-
"C++ types from imported module %2 do not support library evolution}3",
3440+
"C++ types from imported module %2 do not support library evolution|"
3441+
"<<ERROR>>}3",
34413442
(const Decl *, unsigned, Identifier, unsigned))
34423443
ERROR(typealias_desugars_to_type_from_hidden_module,none,
34433444
"%0 aliases '%1.%2' and cannot be used %select{here|"
@@ -3450,7 +3451,8 @@ ERROR(typealias_desugars_to_type_from_hidden_module,none,
34503451
"<<ERROR>>|"
34513452
"%4 was imported for SPI only|"
34523453
"%4 was not imported by this file|"
3453-
"C++ types from imported module %4 do not support library evolution}5",
3454+
"C++ types from imported module %4 do not support library evolution|"
3455+
"%4 was not imported publicly}5",
34543456
(const TypeAliasDecl *, StringRef, StringRef, unsigned, Identifier, unsigned))
34553457
ERROR(conformance_from_implementation_only_module,none,
34563458
"cannot use conformance of %0 to %1 %select{here|as property wrapper here|"
@@ -3462,7 +3464,8 @@ ERROR(conformance_from_implementation_only_module,none,
34623464
"the conformance is declared as SPI|"
34633465
"%3 was imported for SPI only|"
34643466
"%3 was not imported by this file|"
3465-
"C++ types from imported module %3 do not support library evolution}4",
3467+
"C++ types from imported module %3 do not support library evolution|"
3468+
"%3 was not imported publicly}4",
34663469
(Type, Identifier, unsigned, Identifier, unsigned))
34673470
NOTE(assoc_conformance_from_implementation_only_module,none,
34683471
"in associated type %0 (inferred as %1)", (Type, Type))
@@ -6568,7 +6571,8 @@ ERROR(inlinable_decl_ref_from_hidden_module,
65686571
"it is SPI|"
65696572
"%2 was imported for SPI only|"
65706573
"%2 was not imported by this file|"
6571-
"C++ APIs from imported module %2 do not support library evolution}3",
6574+
"C++ APIs from imported module %2 do not support library evolution|"
6575+
"<<ERROR>>}3",
65726576
(const ValueDecl *, unsigned, Identifier, unsigned))
65736577

65746578
WARNING(inlinable_decl_ref_from_hidden_module_warn,
@@ -6585,7 +6589,8 @@ ERROR(inlinable_typealias_desugars_to_type_from_hidden_module,
65856589
"<<ERROR>>|"
65866590
"%4 was imported for SPI only|"
65876591
"%4 was not imported by this file|"
6588-
"C++ types from imported module %4 do not support library evolution}5",
6592+
"C++ types from imported module %4 do not support library evolution|"
6593+
"%4 was not imported publicly}5",
65896594
(const TypeAliasDecl *, StringRef, StringRef, unsigned, Identifier, unsigned))
65906595

65916596
NOTE(missing_import_inserted,

lib/AST/Decl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4330,7 +4330,7 @@ bool ValueDecl::isAccessibleFrom(const DeclContext *useDC,
43304330
[&]() { return getFormalAccess(); });
43314331
}
43324332

4333-
ImportAccessLevel ValueDecl::getImportAccessFrom(const DeclContext *useDC) const {
4333+
ImportAccessLevel Decl::getImportAccessFrom(const DeclContext *useDC) const {
43344334
ModuleDecl *Mod = getModuleContext();
43354335
if (useDC && useDC->getParentModule() != Mod) {
43364336
if (auto useSF = useDC->getParentSourceFile()) {

lib/Sema/ResilienceDiagnostics.cpp

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,31 +152,44 @@ static bool diagnoseTypeAliasDeclRefExportability(SourceLoc loc,
152152

153153
auto definingModule = D->getModuleContext();
154154
auto fragileKind = where.getFragileFunctionKind();
155+
bool warnPreSwift6 = originKind != DisallowedOriginKind::SPIOnly &&
156+
originKind != DisallowedOriginKind::NonPublicImport;
155157
if (fragileKind.kind == FragileFunctionKind::None) {
156158
auto reason = where.getExportabilityReason();
157159
ctx.Diags
158160
.diagnose(loc, diag::typealias_desugars_to_type_from_hidden_module,
159161
TAD, definingModule->getNameStr(), D->getNameStr(),
160162
static_cast<unsigned>(*reason), definingModule->getName(),
161163
static_cast<unsigned>(originKind))
162-
.warnUntilSwiftVersionIf(originKind != DisallowedOriginKind::SPIOnly,
163-
6);
164+
.warnUntilSwiftVersionIf(warnPreSwift6, 6);
164165
} else {
165166
ctx.Diags
166167
.diagnose(loc,
167168
diag::inlinable_typealias_desugars_to_type_from_hidden_module,
168169
TAD, definingModule->getNameStr(), D->getNameStr(),
169170
fragileKind.getSelector(), definingModule->getName(),
170171
static_cast<unsigned>(originKind))
171-
.warnUntilSwiftVersionIf(originKind != DisallowedOriginKind::SPIOnly,
172-
6);
172+
.warnUntilSwiftVersionIf(warnPreSwift6, 6);
173173
}
174174
D->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type);
175175

176176
if (originKind == DisallowedOriginKind::MissingImport &&
177177
!ctx.LangOpts.isSwiftVersionAtLeast(6))
178178
addMissingImport(loc, D, where);
179179

180+
// If limited by an import, note which one.
181+
if (originKind == DisallowedOriginKind::NonPublicImport) {
182+
const DeclContext *DC = where.getDeclContext();
183+
ImportAccessLevel limitImport = D->getImportAccessFrom(DC);
184+
assert(limitImport.has_value() &&
185+
limitImport->accessLevel < AccessLevel::Public &&
186+
"The import should still be non-public");
187+
ctx.Diags.diagnose(limitImport->accessLevelLoc,
188+
diag::decl_import_via_here, D,
189+
limitImport->accessLevel,
190+
limitImport->module.importedModule);
191+
}
192+
180193
return true;
181194
}
182195

@@ -192,6 +205,10 @@ static bool diagnoseValueDeclRefExportability(SourceLoc loc, const ValueDecl *D,
192205
if (originKind == DisallowedOriginKind::None)
193206
return false;
194207

208+
// Access levels from imports are reported with the others access levels.
209+
if (originKind == DisallowedOriginKind::NonPublicImport)
210+
return false;
211+
195212
auto diagName = D->getName();
196213
if (auto accessor = dyn_cast<AccessorDecl>(D)) {
197214
// Only diagnose accessors if their disallowed origin kind differs from
@@ -290,13 +307,27 @@ TypeChecker::diagnoseConformanceExportability(SourceLoc loc,
290307
static_cast<unsigned>(originKind))
291308
.warnUntilSwiftVersionIf((useConformanceAvailabilityErrorsOption &&
292309
!ctx.LangOpts.EnableConformanceAvailabilityErrors &&
293-
originKind != DisallowedOriginKind::SPIOnly) ||
310+
originKind != DisallowedOriginKind::SPIOnly &&
311+
originKind != DisallowedOriginKind::NonPublicImport) ||
294312
originKind == DisallowedOriginKind::MissingImport,
295313
6);
296314

297315
if (originKind == DisallowedOriginKind::MissingImport &&
298316
!ctx.LangOpts.isSwiftVersionAtLeast(6))
299317
addMissingImport(loc, ext, where);
300318

319+
// If limited by an import, note which one.
320+
if (originKind == DisallowedOriginKind::NonPublicImport) {
321+
const DeclContext *DC = where.getDeclContext();
322+
ImportAccessLevel limitImport = ext->getImportAccessFrom(DC);
323+
assert(limitImport.has_value() &&
324+
limitImport->accessLevel < AccessLevel::Public &&
325+
"The import should still be non-public");
326+
ctx.Diags.diagnose(limitImport->accessLevelLoc,
327+
diag::decl_import_via_here, ext,
328+
limitImport->accessLevel,
329+
limitImport->module.importedModule);
330+
}
331+
301332
return true;
302333
}

lib/Sema/TypeCheckAccess.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1915,6 +1915,13 @@ swift::getDisallowedOriginKind(const Decl *decl,
19151915
isFragileClangNode(decl->getClangNode()))
19161916
return DisallowedOriginKind::FragileCxxAPI;
19171917

1918+
// Report non-public import last as it can be ignored by the caller.
1919+
// See \c diagnoseValueDeclRefExportability.
1920+
auto importSource = decl->getImportAccessFrom(where.getDeclContext());
1921+
if (importSource.has_value() &&
1922+
importSource->accessLevel < AccessLevel::Public)
1923+
return DisallowedOriginKind::NonPublicImport;
1924+
19181925
return DisallowedOriginKind::None;
19191926
}
19201927

lib/Sema/TypeCheckAccess.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ enum class DisallowedOriginKind : uint8_t {
4747
SPIOnly,
4848
MissingImport,
4949
FragileCxxAPI,
50+
NonPublicImport,
5051
None
5152
};
5253

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file --leading-lines %s %t
3+
4+
/// Build the libraries.
5+
// RUN: %target-swift-frontend -emit-module %t/ConformanceBaseTypes.swift -o %t
6+
// RUN: %target-swift-frontend -emit-module %t/ConformanceDefinition.swift -o %t -I %t
7+
8+
/// Check diagnostics.
9+
// RUN: %target-swift-frontend -typecheck -verify %t/Client.swift -I %t \
10+
// RUN: -enable-experimental-feature AccessLevelOnImport
11+
12+
//--- ConformanceBaseTypes.swift
13+
public protocol Proto {}
14+
public struct ConformingType {
15+
public init () {}
16+
}
17+
18+
//--- ConformanceDefinition.swift
19+
import ConformanceBaseTypes
20+
extension ConformingType : Proto {}
21+
22+
//--- Client.swift
23+
public import ConformanceBaseTypes
24+
internal import ConformanceDefinition // expected-note 2 {{extension of struct 'ConformingType' imported as 'internal' from 'ConformanceDefinition' here}}
25+
26+
public func useInAPI(a: any Proto = ConformingType()) { // expected-error {{cannot use conformance of 'ConformingType' to 'Proto' here; 'ConformanceDefinition' was not imported publicly}}
27+
}
28+
29+
@inlinable public func inlinableFunc() {
30+
let _: any Proto = ConformingType() // expected-error {{cannot use conformance of 'ConformingType' to 'Proto' here; 'ConformanceDefinition' was not imported publicly}}
31+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
// RUN: %target-swift-emit-module-interface(%t/Original.swiftinterface) %t/Original.swift
5+
// RUN: %target-swift-typecheck-module-from-interface(%t/Original.swiftinterface)
6+
7+
// RUN: %target-swift-emit-module-interface(%t/Aliases.swiftinterface) %t/Aliases.swift -I %t
8+
// RUN: %target-swift-typecheck-module-from-interface(%t/Aliases.swiftinterface) -I %t
9+
10+
// RUN: %target-swift-frontend -typecheck -verify %t/UsesAliasesNoImport.swift -I %t \
11+
// RUN: -swift-version 5 -enable-library-evolution \
12+
// RUN: -enable-experimental-feature AccessLevelOnImport
13+
14+
//--- Original.swift
15+
open class Clazz {}
16+
17+
//--- Aliases.swift
18+
import Original
19+
public typealias ClazzAlias = Clazz
20+
21+
//--- UsesAliasesNoImport.swift
22+
public import Aliases
23+
internal import Original // expected-note 2 {{class 'Clazz' imported as 'internal' from 'Original' here}}
24+
25+
// expected-error@+1 {{'ClazzAlias' aliases 'Original.Clazz' and cannot be used here because 'Original' was not imported publicly}}
26+
public class InheritsFromClazzAlias: ClazzAlias {}
27+
28+
@inlinable public func inlinableFunc() {
29+
// expected-error@+1 {{'ClazzAlias' aliases 'Original.Clazz' and cannot be used in an '@inlinable' function because 'Original' was not imported publicly}}
30+
_ = ClazzAlias.self
31+
}
32+

0 commit comments

Comments
 (0)