Skip to content

Commit 9127f90

Browse files
committed
[Sema] Report uses of implicitly imported decls in inlinable code
Implicitly imported decls may end up in inlinable code and break the module API. This have been known to lead to deserialization crash and could in theory break the generated swiftinterfaces files. Let's explicitly check for such a case, keeping it to a warning until Swift 6 where we can make it an error. rdar://95816286
1 parent 4d9c8ee commit 9127f90

7 files changed

+121
-8
lines changed

include/swift/AST/DiagnosticsSema.def

+14-3
Original file line numberDiff line numberDiff line change
@@ -2903,14 +2903,17 @@ ERROR(decl_from_hidden_module,none,
29032903
"in an extension with conditional conformances}2; "
29042904
"%select{%3 has been imported as implementation-only|"
29052905
"it is an SPI imported from %3|"
2906-
"it is SPI}4",
2906+
"it is SPI|"
2907+
"%3 was implicitly imported}4",
29072908
(DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned))
29082909
WARNING(decl_from_hidden_module_warn,none,
29092910
"cannot use %0 %1 %select{here|as property wrapper here|"
29102911
"as result builder here|"
29112912
"in an extension with public or '@usableFromInline' members|"
29122913
"in an extension with conditional conformances}2; "
2913-
"%select{%3 has been imported as implementation-only}4",
2914+
"%select{%3 has been imported as implementation-only|"
2915+
"<<ERROR>>|<<ERROR>>|"
2916+
"%3 was implicitly imported}4",
29142917
(DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned))
29152918
ERROR(conformance_from_implementation_only_module,none,
29162919
"cannot use conformance of %0 to %1 %select{here|as property wrapper here|"
@@ -5781,7 +5784,15 @@ ERROR(inlinable_decl_ref_from_hidden_module,
57815784
none, "%0 %1 cannot be used in " FRAGILE_FUNC_KIND "2 "
57825785
"because %select{%3 was imported implementation-only|"
57835786
"it is an SPI imported from %3|"
5784-
"it is SPI}4",
5787+
"it is SPI|"
5788+
"%3 was implicitly imported}4",
5789+
(DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned))
5790+
5791+
WARNING(inlinable_decl_ref_from_hidden_module_warn,
5792+
none, "%0 %1 cannot be used in " FRAGILE_FUNC_KIND "2 "
5793+
"because %select{<<ERROR>>|<<ERROR>>|<<ERROR>>|"
5794+
"%3 was implicitly imported}4"
5795+
"; this is an error in Swift 6",
57855796
(DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned))
57865797

57875798
ERROR(availability_macro_in_inlinable, none,

include/swift/AST/SourceFile.h

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class PersistentParserState;
3131
/// \sa getRestrictedImportKind
3232
enum class RestrictedImportKind {
3333
ImplementationOnly,
34+
Implicit,
3435
None // No restriction, i.e. the module is imported publicly.
3536
};
3637

lib/AST/Module.cpp

+7-3
Original file line numberDiff line numberDiff line change
@@ -2440,14 +2440,18 @@ bool SourceFile::hasTestableOrPrivateImport(
24402440

24412441
RestrictedImportKind SourceFile::getRestrictedImportKind(const ModuleDecl *module) const {
24422442
auto &imports = getASTContext().getImportCache();
2443+
RestrictedImportKind importKind = RestrictedImportKind::Implicit;
24432444

24442445
// Look at the imports of this source file.
24452446
for (auto &desc : *Imports) {
24462447
// Ignore implementation-only imports.
2447-
if (desc.options.contains(ImportFlags::ImplementationOnly))
2448+
if (desc.options.contains(ImportFlags::ImplementationOnly)) {
2449+
if (imports.isImportedBy(module, desc.module.importedModule))
2450+
importKind = RestrictedImportKind::ImplementationOnly;
24482451
continue;
2452+
}
24492453

2450-
// If the module is imported this way, it's not imported
2454+
// If the module is imported publicly, it's not imported
24512455
// implementation-only.
24522456
if (imports.isImportedBy(module, desc.module.importedModule))
24532457
return RestrictedImportKind::None;
@@ -2457,7 +2461,7 @@ RestrictedImportKind SourceFile::getRestrictedImportKind(const ModuleDecl *modul
24572461
if (imports.isImportedBy(module, getParentModule()))
24582462
return RestrictedImportKind::None;
24592463

2460-
return RestrictedImportKind::ImplementationOnly;
2464+
return importKind;
24612465
}
24622466

24632467
bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module) const {

lib/Sema/ResilienceDiagnostics.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,16 @@ TypeChecker::diagnoseDeclRefExportability(SourceLoc loc,
146146

147147
D->diagnose(diag::kind_declared_here, DescriptiveDeclKind::Type);
148148
} else {
149-
ctx.Diags.diagnose(loc, diag::inlinable_decl_ref_from_hidden_module,
149+
// Only implicitly imported decls should be reported as a warning,
150+
// and only for language versions below Swift 6.
151+
assert(downgradeToWarning == DowngradeToWarning::No ||
152+
originKind == DisallowedOriginKind::ImplicitlyImported &&
153+
"Only implicitly imported decls should be reported as a warning.");
154+
auto errorOrWarning = downgradeToWarning == DowngradeToWarning::Yes?
155+
diag::inlinable_decl_ref_from_hidden_module_warn:
156+
diag::inlinable_decl_ref_from_hidden_module;
157+
158+
ctx.Diags.diagnose(loc, errorOrWarning,
150159
D->getDescriptiveKind(), D->getName(),
151160
fragileKind.getSelector(), definingModule->getName(),
152161
static_cast<unsigned>(originKind));

lib/Sema/TypeCheckAccess.cpp

+13-1
Original file line numberDiff line numberDiff line change
@@ -1511,6 +1511,15 @@ swift::getDisallowedOriginKind(const Decl *decl,
15111511
if (where.isSPI())
15121512
downgradeToWarning = DowngradeToWarning::Yes;
15131513

1514+
// Before Swift 6, implicit imports were not reported unless an
1515+
// implementation-only import was also present. Downgrade to a warning
1516+
// just in this case.
1517+
if (howImported == RestrictedImportKind::Implicit &&
1518+
!SF->getASTContext().isSwiftVersionAtLeast(6) &&
1519+
!SF->hasImplementationOnlyImports()) {
1520+
downgradeToWarning = DowngradeToWarning::Yes;
1521+
}
1522+
15141523
// Even if the current module is @_implementationOnly, Swift should
15151524
// not report an error in the cases where the decl is also exported from
15161525
// a non @_implementationOnly module. Thus, we check to see if there is
@@ -1545,7 +1554,10 @@ swift::getDisallowedOriginKind(const Decl *decl,
15451554
}
15461555
}
15471556
}
1548-
// Implementation-only imported, cannot be reexported.
1557+
1558+
// Restrictively imported, cannot be reexported.
1559+
if (howImported == RestrictedImportKind::Implicit)
1560+
return DisallowedOriginKind::ImplicitlyImported;
15491561
return DisallowedOriginKind::ImplementationOnly;
15501562
} else if ((decl->isSPI() || decl->isAvailableAsSPI()) && !where.isSPI()) {
15511563
if (decl->isAvailableAsSPI() && !decl->isSPI()) {

lib/Sema/TypeCheckAccess.h

+2
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ void checkAccessControl(Decl *D);
3535
// Problematic origin of an exported type.
3636
//
3737
// This enum must be kept in sync with
38+
// diag::inlinable_decl_ref_from_hidden_module,
3839
// diag::decl_from_hidden_module and
3940
// diag::conformance_from_implementation_only_module.
4041
enum class DisallowedOriginKind : uint8_t {
4142
ImplementationOnly,
4243
SPIImported,
4344
SPILocal,
45+
ImplicitlyImported,
4446
None
4547
};
4648

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/// Report the use in API of indirectly or implicitly imported decls.
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: %{python} %utils/split_file.py -o %t %s
5+
6+
// RUN: %target-swift-frontend -emit-module %t/empty.swift -module-name empty -o %t/empty.swiftmodule
7+
// RUN: %target-swift-frontend -emit-module %t/libA.swift -module-name libA -o %t/libA.swiftmodule
8+
// RUN: %target-swift-frontend -emit-module %t/libB.swift -module-name libB -o %t/libB.swiftmodule -I %t
9+
10+
/// In pre-Swift 6, this is a warning where there's no implementation-only import present.
11+
// RUN: %target-swift-frontend -emit-module %t/clientFileA-Swift5.swift %t/clientFileB.swift -module-name client -o %t/client.swiftmodule -I %t -verify
12+
13+
/// In pre-Swift 6, this remains an error when there's an implementation-only import present.
14+
// RUN: %target-swift-frontend -emit-module %t/clientFileA-OldCheck.swift %t/clientFileB.swift -module-name client -o %t/client.swiftmodule -I %t -verify
15+
16+
/// In Swift 6, it's an error.
17+
// RUN: %target-swift-frontend -emit-module %t/clientFileA-Swift6.swift %t/clientFileB.swift -module-name client -o %t/client.swiftmodule -I %t -verify -swift-version 6
18+
19+
// BEGIN empty.swift
20+
21+
// BEGIN libA.swift
22+
public struct ImportedType {
23+
public init() {}
24+
}
25+
26+
// BEGIN libB.swift
27+
import libA
28+
29+
extension ImportedType {
30+
public func implicitlyImportedMethod() {}
31+
}
32+
33+
/// Client module
34+
// BEGIN clientFileA-Swift5.swift
35+
import libA
36+
37+
@inlinable public func bar() {
38+
let a = ImportedType()
39+
a.implicitlyImportedMethod() // expected-warning {{instance method 'implicitlyImportedMethod()' cannot be used in an '@inlinable' function because 'libB' was implicitly imported; this is an error in Swift 6}}
40+
41+
// Expected implicit imports are still fine
42+
a.localModuleMethod()
43+
}
44+
45+
// BEGIN clientFileA-OldCheck.swift
46+
import libA
47+
@_implementationOnly import empty
48+
49+
@inlinable public func bar() {
50+
let a = ImportedType()
51+
a.implicitlyImportedMethod() // expected-error {{instance method 'implicitlyImportedMethod()' cannot be used in an '@inlinable' function because 'libB' was implicitly imported}}
52+
53+
// Expected implicit imports are still fine
54+
a.localModuleMethod()
55+
}
56+
57+
// BEGIN clientFileA-Swift6.swift
58+
import libA
59+
60+
@inlinable public func bar() {
61+
let a = ImportedType()
62+
a.implicitlyImportedMethod() // expected-error {{instance method 'implicitlyImportedMethod()' cannot be used in an '@inlinable' function because 'libB' was implicitly imported}}
63+
64+
// Expected implicit imports are still fine
65+
a.localModuleMethod()
66+
}
67+
68+
// BEGIN clientFileB.swift
69+
@_implementationOnly import libB
70+
import libA
71+
extension ImportedType {
72+
public func localModuleMethod() {}
73+
}
74+

0 commit comments

Comments
 (0)