Skip to content

Commit c3867db

Browse files
committed
[Sema] Report importing modules built from a swiftinterface without resilience
Generating a swiftinterface for a module without library-evolution is not currently a recommended configuration, and it leads to a warning at building the module. We’re now considering generating more swiftinterface even without library evolution as the swiftinterface can be useful for documentation tools. However, while we can build against such a swiftinterface we shouldn’t expect the executable to work reliably. This PR adds the infrastructure necessary to detect when we’re importing a module from a swiftinterface without it having enabled library-evoluation. It is reported as an error which can be downgraded to a warning with an env var. Compiler engineers may still want a full build even if the binary is unusable. rdar://101637436
1 parent 51c5da0 commit c3867db

File tree

3 files changed

+87
-5
lines changed

3 files changed

+87
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,11 @@ ERROR(module_allowable_client_violation,none,
988988
"module %0 doesn't allow importation from module %1",
989989
(Identifier, Identifier))
990990

991+
ERROR(import_not_compiled_with_library_evolution_but_rebuilt,none,
992+
"module %0 was rebuilt from a swiftinterface without library evolution; "
993+
"it cannot be used to build a binary",
994+
(Identifier))
995+
991996
REMARK(cross_import_added,none,
992997
"import of %0 and %1 triggered a cross-import of %2",
993998
(Identifier, Identifier, Identifier))

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,24 +1923,26 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
19231923

19241924
void visitImportDecl(ImportDecl *ID) {
19251925
TypeChecker::checkDeclAttributes(ID);
1926+
ModuleDecl *importTarget = ID->getModule();
1927+
if (!importTarget)
1928+
return;
19261929

19271930
// Force the lookup of decls referenced by a scoped import in case it emits
19281931
// diagnostics.
19291932
(void)ID->getDecls();
19301933

19311934
// Report the public import of a private module.
19321935
if (ID->getASTContext().LangOpts.LibraryLevel == LibraryLevel::API) {
1933-
auto target = ID->getModule();
19341936
auto importer = ID->getModuleContext();
1935-
if (target &&
1937+
if (importTarget &&
19361938
!ID->getAttrs().hasAttribute<ImplementationOnlyAttr>() &&
19371939
!ID->getAttrs().hasAttribute<SPIOnlyAttr>() &&
1938-
target->getLibraryLevel() == LibraryLevel::SPI) {
1940+
importTarget->getLibraryLevel() == LibraryLevel::SPI) {
19391941

19401942
auto &diags = ID->getASTContext().Diags;
19411943
InFlightDiagnostic inFlight =
19421944
diags.diagnose(ID, diag::error_public_import_of_private_module,
1943-
target->getName(), importer->getName());
1945+
importTarget->getName(), importer->getName());
19441946
if (ID->getAttrs().isEmpty()) {
19451947
inFlight.fixItInsert(ID->getStartLoc(),
19461948
"@_implementationOnly ");
@@ -1952,7 +1954,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
19521954
static bool enableTreatAsError = getenv("ENABLE_PUBLIC_IMPORT_OF_PRIVATE_AS_ERROR");
19531955
#endif
19541956

1955-
bool isImportOfUnderlying = importer->getName() == target->getName();
1957+
bool isImportOfUnderlying = importer->getName() ==
1958+
importTarget->getName();
19561959
auto *SF = ID->getDeclContext()->getParentSourceFile();
19571960
bool treatAsError = enableTreatAsError &&
19581961
!isImportOfUnderlying &&
@@ -1961,6 +1964,21 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
19611964
inFlight.limitBehavior(DiagnosticBehavior::Warning);
19621965
}
19631966
}
1967+
1968+
// Report imports of non-resilient modules built from a module interface.
1969+
if (importTarget->isBuiltFromInterface() &&
1970+
importTarget->getResilienceStrategy() != ResilienceStrategy::Resilient) {
1971+
auto &diags = ID->getASTContext().Diags;
1972+
auto inFlight =
1973+
diags.diagnose(ID,
1974+
diag::import_not_compiled_with_library_evolution_but_rebuilt,
1975+
importTarget->getName());
1976+
1977+
static const char* acceptNonResilientInterfaes =
1978+
::getenv("SWIFT_ACCEPT_NON_RESILIENT_INTERFACES");
1979+
if (acceptNonResilientInterfaes)
1980+
inFlight.limitBehavior(DiagnosticBehavior::Warning);
1981+
}
19641982
}
19651983

19661984
void visitOperatorDecl(OperatorDecl *OD) {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
// RUN: %target-swift-frontend -emit-module %t/NonResilientLib.swift \
5+
// RUN: -module-name NonResilientLib -swift-version 5 \
6+
// RUN: -emit-module-path %t/NonResilientLib.swiftmodule \
7+
// RUN: -emit-module-interface-path %t/NonResilientLib.swiftinterface 2>&1 \
8+
// RUN: | grep "warning: module interfaces are only supported with -enable-library-evolution"
9+
10+
// RUN: %target-swift-frontend -emit-module %t/ResilientLib.swift \
11+
// RUN: -module-name ResilientLib -swift-version 5 \
12+
// RUN: -enable-library-evolution \
13+
// RUN: -emit-module-path %t/ResilientLib.swiftmodule \
14+
// RUN: -emit-module-interface-path %t/ResilientLib.swiftinterface
15+
16+
/// Expecting no diagnostics when importing the swiftmodule.
17+
// RUN: %target-swift-frontend -typecheck %t/ClientClean.swift \
18+
// RUN: -swift-version 5 -I %t -verify
19+
20+
/// Expecting diagnostics when importing the non-resilient swiftinterface.
21+
// RUN: rm %t/*.swiftmodule
22+
// RUN: %target-swift-frontend -typecheck %t/ClientError.swift \
23+
// RUN: -swift-version 5 -I %t -verify
24+
25+
// RUN: env SWIFT_ACCEPT_NON_RESILIENT_INTERFACES=1 \
26+
// RUN: %target-swift-frontend -typecheck %t/ClientWarning.swift \
27+
// RUN: -swift-version 5 -I %t -verify
28+
29+
//--- NonResilientLib.swift
30+
31+
public func NonResilientFunc() {}
32+
33+
//--- ResilientLib.swift
34+
35+
public func ResilientFunc() {}
36+
37+
//--- ClientClean.swift
38+
39+
import NonResilientLib
40+
import ResilientLib
41+
42+
NonResilientFunc()
43+
ResilientFunc()
44+
45+
//--- ClientError.swift
46+
47+
import NonResilientLib // expected-error {{module 'NonResilientLib' was rebuilt from a swiftinterface without library evolution; it cannot be used to build a binary}}
48+
import ResilientLib
49+
50+
NonResilientFunc()
51+
ResilientFunc()
52+
53+
//--- ClientWarning.swift
54+
55+
import NonResilientLib // expected-warning {{module 'NonResilientLib' was rebuilt from a swiftinterface without library evolution; it cannot be used to build a binary}}
56+
import ResilientLib
57+
58+
NonResilientFunc()
59+
ResilientFunc()

0 commit comments

Comments
 (0)