Skip to content

Commit 4feb560

Browse files
authored
Merge pull request swiftlang#39304 from xymus/precise-version
[Serialization] Restrict resilient swiftmodules to the compiler that built them
2 parents 0b38780 + 362cacf commit 4feb560

File tree

10 files changed

+124
-6
lines changed

10 files changed

+124
-6
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,10 @@ ERROR(serialization_module_too_old,Fatal,
766766
"compiled module was created by an older version of the compiler; "
767767
"rebuild %0 and try again: %1",
768768
(Identifier, StringRef))
769+
ERROR(serialization_module_incompatible_revision,Fatal,
770+
"compiled module was created by a different version of the compiler; "
771+
"rebuild %0 and try again: %1",
772+
(Identifier, StringRef))
769773
ERROR(serialization_missing_single_dependency,Fatal,
770774
"missing required module '%0'", (StringRef))
771775
ERROR(serialization_missing_dependencies,Fatal,

include/swift/Basic/Version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ std::string getSwiftFullVersion(Version effectiveLanguageVersion =
181181

182182
/// Retrieves the repository revision number (or identifier) from which
183183
/// this Swift was built.
184-
std::string getSwiftRevision();
184+
StringRef getSwiftRevision();
185185

186186
} // end namespace version
187187
} // end namespace swift

include/swift/Serialization/Validation.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ enum class Status {
3939
/// compiler.
4040
FormatTooNew,
4141

42+
/// The precise revision version doesn't match.
43+
RevisionIncompatible,
44+
4245
/// The module file depends on another module that can't be loaded.
4346
MissingDependency,
4447

lib/Basic/Version.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ std::string getSwiftFullVersion(Version effectiveVersion) {
438438
return OS.str();
439439
}
440440

441-
std::string getSwiftRevision() {
441+
StringRef getSwiftRevision() {
442442
#ifdef SWIFT_REVISION
443443
return SWIFT_REVISION;
444444
#else

lib/Serialization/ModuleFile.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ Status ModuleFile::associateWithFileContext(FileUnit *file, SourceLoc diagLoc,
149149
return error(status);
150150
}
151151

152-
auto clientSDK = ctx.LangOpts.SDKName;
153152
StringRef moduleSDK = Core->SDKName;
153+
StringRef clientSDK = ctx.LangOpts.SDKName;
154154
if (ctx.SearchPathOpts.EnableSameSDKCheck &&
155155
!moduleSDK.empty() && !clientSDK.empty() &&
156156
moduleSDK != clientSDK) {

lib/Serialization/ModuleFileSharedCore.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,35 @@ validateControlBlock(llvm::BitstreamCursor &cursor,
303303
result.sdkName = blobData;
304304
break;
305305
}
306+
case control_block::REVISION: {
307+
// Tagged compilers should load only resilient modules if they were
308+
// produced by the exact same version.
309+
310+
// Disable this restriction for compiler testing by setting this
311+
// env var to any value.
312+
static const char* ignoreRevision =
313+
::getenv("SWIFT_DEBUG_IGNORE_SWIFTMODULE_REVISION");
314+
if (ignoreRevision)
315+
break;
316+
317+
// Override this env var for testing, forcing the behavior of a tagged
318+
// compiler and using the env var value to override this compiler's
319+
// revision.
320+
static const char* forcedDebugRevision =
321+
::getenv("SWIFT_DEBUG_FORCE_SWIFTMODULE_REVISION");
322+
323+
bool isCompilerTagged = forcedDebugRevision ||
324+
- !version::Version::getCurrentCompilerVersion().empty();
325+
326+
StringRef moduleRevision = blobData;
327+
if (isCompilerTagged && !moduleRevision.empty()) {
328+
StringRef compilerRevision = forcedDebugRevision ?
329+
forcedDebugRevision : version::getSwiftRevision();
330+
if (moduleRevision != compilerRevision)
331+
result.status = Status::RevisionIncompatible;
332+
}
333+
break;
334+
}
306335
default:
307336
// Unknown metadata record, possibly for use by a future version of the
308337
// module format.

lib/Serialization/ModuleFormat.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5656
/// describe what change you made. The content of this comment isn't important;
5757
/// it just ensures a conflict if two people change the module format.
5858
/// Don't worry about adhering to the 80-column limit for this line.
59-
const uint16_t SWIFTMODULE_VERSION_MINOR = 629; // BuilderSDK
59+
const uint16_t SWIFTMODULE_VERSION_MINOR = 630; // Precise
6060

6161
/// A standard hash seed used for all string hashes in a serialized module.
6262
///
@@ -755,7 +755,8 @@ namespace control_block {
755755
METADATA = 1,
756756
MODULE_NAME,
757757
TARGET,
758-
SDK_NAME
758+
SDK_NAME,
759+
REVISION,
759760
};
760761

761762
using MetadataLayout = BCRecordLayout<
@@ -785,6 +786,11 @@ namespace control_block {
785786
SDK_NAME,
786787
BCBlob
787788
>;
789+
790+
using RevisionLayout = BCRecordLayout<
791+
REVISION,
792+
BCBlob
793+
>;
788794
}
789795

790796
/// The record types within the options block (a sub-block of the control

lib/Serialization/Serialization.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,7 @@ void Serializer::writeBlockInfoBlock() {
803803
BLOCK_RECORD(control_block, MODULE_NAME);
804804
BLOCK_RECORD(control_block, TARGET);
805805
BLOCK_RECORD(control_block, SDK_NAME);
806+
BLOCK_RECORD(control_block, REVISION);
806807

807808
BLOCK(OPTIONS_BLOCK);
808809
BLOCK_RECORD(options_block, SDK_PATH);
@@ -949,11 +950,12 @@ void Serializer::writeBlockInfoBlock() {
949950

950951
void Serializer::writeHeader(const SerializationOptions &options) {
951952
{
952-
BCBlockRAII restoreBlock(Out, CONTROL_BLOCK_ID, 3);
953+
BCBlockRAII restoreBlock(Out, CONTROL_BLOCK_ID, 4);
953954
control_block::ModuleNameLayout ModuleName(Out);
954955
control_block::MetadataLayout Metadata(Out);
955956
control_block::TargetLayout Target(Out);
956957
control_block::SDKNameLayout SDKName(Out);
958+
control_block::RevisionLayout Revision(Out);
957959

958960
ModuleName.emit(ScratchRecord, M->getName().str());
959961

@@ -992,6 +994,18 @@ void Serializer::writeHeader(const SerializationOptions &options) {
992994

993995
Target.emit(ScratchRecord, M->getASTContext().LangOpts.Target.str());
994996

997+
// Write the producer's Swift revision only for resilient modules.
998+
if (M->getResilienceStrategy() != ResilienceStrategy::Default) {
999+
auto revision = version::getSwiftRevision();
1000+
1001+
static const char* forcedDebugRevision =
1002+
::getenv("SWIFT_DEBUG_FORCE_SWIFTMODULE_REVISION");
1003+
if (forcedDebugRevision)
1004+
revision = forcedDebugRevision;
1005+
1006+
Revision.emit(ScratchRecord, revision);
1007+
}
1008+
9951009
{
9961010
llvm::BCBlockRAII restoreBlock(Out, OPTIONS_BLOCK_ID, 4);
9971011

lib/Serialization/SerializedModuleLoader.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,10 @@ void swift::serialization::diagnoseSerializedASTLoadFailure(
839839
Ctx.Diags.diagnose(diagLoc, diag::serialization_module_too_old, ModuleName,
840840
moduleBufferID);
841841
break;
842+
case serialization::Status::RevisionIncompatible:
843+
Ctx.Diags.diagnose(diagLoc, diag::serialization_module_incompatible_revision,
844+
ModuleName, moduleBufferID);
845+
break;
842846
case serialization::Status::Malformed:
843847
Ctx.Diags.diagnose(diagLoc, diag::serialization_malformed_module,
844848
moduleBufferID);
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// RUN: %empty-directory(%t/cache)
2+
// RUN: %empty-directory(%t/build)
3+
// RUN: %{python} %utils/split_file.py -o %t %s
4+
5+
/// 1. Compile Lib.swift as non-resilient untagged, resilient untagged, and resilient tagged
6+
// BEGIN Lib.swift
7+
public func foo() {}
8+
9+
/// Build Lib as a resilient and non-resilient swiftmodule
10+
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -swift-version 5 -o %t/build -parse-stdlib -module-cache-path %t/cache -module-name ResilientLib -enable-library-evolution
11+
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -swift-version 5 -o %t/build -parse-stdlib -module-cache-path %t/cache -module-name NonresilientLib
12+
// RUN: SWIFT_DEBUG_FORCE_SWIFTMODULE_REVISION=my-revision \
13+
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -swift-version 5 -o %t/build -parse-stdlib -module-cache-path %t/cache -module-name TaggedLib -enable-library-evolution
14+
15+
16+
/// 2. Test importing the non-resilient untagged library
17+
// BEGIN NonresilientClient.swift
18+
import NonresilientLib
19+
foo()
20+
21+
/// Building a NonresilientLib client should always succeed
22+
// RUN: %target-swift-frontend -typecheck %t/NonresilientClient.swift -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
23+
// RUN: SWIFT_DEBUG_FORCE_SWIFTMODULE_REVISION=my-revision \
24+
// RUN: %target-swift-frontend -typecheck %t/NonresilientClient.swift -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
25+
26+
27+
/// 3. Test importing the resilient untagged library
28+
// BEGIN ResilientClient.swift
29+
import ResilientLib
30+
foo()
31+
32+
/// Building a ResilientLib client should succeed in non-revision/dev mode
33+
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
34+
35+
/// Building a ResilientLib client should reject the import for a tagged compiler
36+
// RUN: SWIFT_DEBUG_FORCE_SWIFTMODULE_REVISION=not-a-revision \
37+
// RUN: not %target-swift-frontend -typecheck %t/ResilientClient.swift -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache 2>&1 | %FileCheck %s
38+
// CHECK: compiled module was created by a different version of the compiler; rebuild 'ResilientLib' and try again: {{.*}}ResilientLib.swiftmodule
39+
40+
/// Building a ResilientLib client should succeed for a tagged compiler with SWIFT_DEBUG_IGNORE_SWIFTMODULE_REVISION
41+
// RUN: SWIFT_DEBUG_FORCE_SWIFTMODULE_REVISION=not-a-revision SWIFT_DEBUG_IGNORE_SWIFTMODULE_REVISION=true \
42+
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
43+
44+
45+
/// 4. Test importing the resilient tagged library
46+
// BEGIN TaggedClient.swift
47+
import TaggedLib
48+
foo()
49+
50+
/// Importing TaggedLib should success with the same tag or a dev compiler
51+
// RUN: SWIFT_DEBUG_FORCE_SWIFTMODULE_REVISION=my-revision \
52+
// RUN: %target-swift-frontend -typecheck %t/TaggedClient.swift -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
53+
// RUN: %target-swift-frontend -typecheck %t/TaggedClient.swift -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
54+
55+
/// Building a TaggedLib client should reject the import for a different tagged compiler
56+
// RUN: SWIFT_DEBUG_FORCE_SWIFTMODULE_REVISION=not-a-revision \
57+
// RUN: not %target-swift-frontend -typecheck %t/TaggedClient.swift -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache 2>&1 | %FileCheck %s --check-prefix=CHECK-TAGGED
58+
// CHECK-TAGGED: compiled module was created by a different version of the compiler; rebuild 'TaggedLib' and try again: {{.*}}TaggedLib.swiftmodule

0 commit comments

Comments
 (0)