Skip to content

Commit cf9e3fd

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 41ec454 commit cf9e3fd

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
@@ -984,6 +984,11 @@ WARNING(module_not_compiled_with_library_evolution,none,
984984
"using it means binary compatibility for %1 can't be guaranteed",
985985
(Identifier, Identifier))
986986

987+
ERROR(import_not_compiled_with_library_evolution_but_rebuilt,none,
988+
"module %0 was rebuilt from its swiftinterface but not compiled with "
989+
"library evolution support; execution will be unreliable",
990+
(Identifier))
991+
987992
REMARK(cross_import_added,none,
988993
"import of %0 and %1 triggered a cross-import of %2",
989994
(Identifier, Identifier, Identifier))

lib/Sema/TypeCheckDeclPrimary.cpp

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

19141914
void visitImportDecl(ImportDecl *ID) {
19151915
TypeChecker::checkDeclAttributes(ID);
1916+
ModuleDecl *importTarget = ID->getModule();
1917+
if (!importTarget)
1918+
return;
19161919

19171920
// Force the lookup of decls referenced by a scoped import in case it emits
19181921
// diagnostics.
19191922
(void)ID->getDecls();
19201923

19211924
// Report the public import of a private module.
19221925
if (ID->getASTContext().LangOpts.LibraryLevel == LibraryLevel::API) {
1923-
auto target = ID->getModule();
19241926
auto importer = ID->getModuleContext();
1925-
if (target &&
1927+
if (importTarget &&
19261928
!ID->getAttrs().hasAttribute<ImplementationOnlyAttr>() &&
19271929
!ID->getAttrs().hasAttribute<SPIOnlyAttr>() &&
1928-
target->getLibraryLevel() == LibraryLevel::SPI) {
1930+
importTarget->getLibraryLevel() == LibraryLevel::SPI) {
19291931

19301932
auto &diags = ID->getASTContext().Diags;
19311933
InFlightDiagnostic inFlight =
19321934
diags.diagnose(ID, diag::error_public_import_of_private_module,
1933-
target->getName(), importer->getName());
1935+
importTarget->getName(), importer->getName());
19341936
if (ID->getAttrs().isEmpty()) {
19351937
inFlight.fixItInsert(ID->getStartLoc(),
19361938
"@_implementationOnly ");
@@ -1942,7 +1944,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
19421944
static bool enableTreatAsError = getenv("ENABLE_PUBLIC_IMPORT_OF_PRIVATE_AS_ERROR");
19431945
#endif
19441946

1945-
bool isImportOfUnderlying = importer->getName() == target->getName();
1947+
bool isImportOfUnderlying = importer->getName() ==
1948+
importTarget->getName();
19461949
auto *SF = ID->getDeclContext()->getParentSourceFile();
19471950
bool treatAsError = enableTreatAsError &&
19481951
!isImportOfUnderlying &&
@@ -1951,6 +1954,21 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
19511954
inFlight.limitBehavior(DiagnosticBehavior::Warning);
19521955
}
19531956
}
1957+
1958+
// Report imports of non-resilient modules built from a module interface.
1959+
if (importTarget->isBuiltFromInterface() &&
1960+
importTarget->getResilienceStrategy() != ResilienceStrategy::Resilient) {
1961+
auto &diags = ID->getASTContext().Diags;
1962+
auto inFlight =
1963+
diags.diagnose(ID,
1964+
diag::import_not_compiled_with_library_evolution_but_rebuilt,
1965+
importTarget->getName());
1966+
1967+
static const char* acceptNonResilientInterfaes =
1968+
::getenv("SWIFT_ACCEPT_NON_RESILIENT_INTERFACES");
1969+
if (acceptNonResilientInterfaes)
1970+
inFlight.limitBehavior(DiagnosticBehavior::Warning);
1971+
}
19541972
}
19551973

19561974
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 its swiftinterface but not compiled with library evolution support; execution will be unreliable}}
48+
import ResilientLib
49+
50+
NonResilientFunc()
51+
ResilientFunc()
52+
53+
//--- ClientWarning.swift
54+
55+
import NonResilientLib // expected-warning {{module 'NonResilientLib' was rebuilt from its swiftinterface but not compiled with library evolution support; execution will be unreliable}}
56+
import ResilientLib
57+
58+
NonResilientFunc()
59+
ResilientFunc()

0 commit comments

Comments
 (0)