Skip to content

Commit 70cf5d4

Browse files
authored
Merge pull request #72001 from xymus/restrict-to-channel
Serialization: Restrict swiftmodules to distribution channels
2 parents 6110ab9 + 7edd23a commit 70cf5d4

File tree

10 files changed

+163
-1
lines changed

10 files changed

+163
-1
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,11 @@ ERROR(serialization_module_incompatible_revision,Fatal,
850850
"compiled module was created by a different version of the compiler '%0'; "
851851
"rebuild %1 and try again: %2",
852852
(StringRef, Identifier, StringRef))
853+
ERROR(serialization_module_incompatible_channel,Fatal,
854+
"compiled module was created for a different distribution channel '%0' "
855+
"than the local compiler '%1', "
856+
"please ensure %2 is found from the expected path: %3",
857+
(StringRef, StringRef, Identifier, StringRef))
853858
ERROR(serialization_missing_single_dependency,Fatal,
854859
"missing required module '%0'", (StringRef))
855860
ERROR(serialization_missing_dependencies,Fatal,

include/swift/Basic/Version.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,11 @@ StringRef getCurrentCompilerTag();
177177
/// depending on the vendor.
178178
StringRef getCurrentCompilerSerializationTag();
179179

180+
/// Distribution channel of the running compiler for distributed swiftmodules.
181+
/// Helps to distinguish swiftmodules between different compilers using the
182+
/// same serialization tag.
183+
StringRef getCurrentCompilerChannel();
184+
180185
/// Retrieves the value of the upcoming C++ interoperability compatibility
181186
/// version that's going to be presented as some new concrete version to the
182187
/// users.

include/swift/Serialization/Validation.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ enum class Status {
4848
/// The precise revision version doesn't match.
4949
RevisionIncompatible,
5050

51+
/// The distribution channel doesn't match.
52+
ChannelIncompatible,
53+
5154
/// The module is required to be in OSSA, but is not.
5255
NotInOSSA,
5356

@@ -105,6 +108,7 @@ struct ValidationInfo {
105108
llvm::VersionTuple userModuleVersion;
106109
StringRef sdkName = {};
107110
StringRef problematicRevision = {};
111+
StringRef problematicChannel = {};
108112
size_t bytes = 0;
109113
Status status = Status::Malformed;
110114
std::vector<StringRef> allowableClients;

lib/Basic/Version.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,16 @@ StringRef getCurrentCompilerSerializationTag() {
313313
#endif
314314
}
315315

316+
StringRef getCurrentCompilerChannel() {
317+
static const char* forceDebugChannel =
318+
::getenv("SWIFT_FORCE_SWIFTMODULE_CHANNEL");
319+
if (forceDebugChannel)
320+
return forceDebugChannel;
321+
322+
// Leave it to downstream compilers to define the different channels.
323+
return StringRef();
324+
}
325+
316326
unsigned getUpcomingCxxInteropCompatVersion() {
317327
return SWIFT_VERSION_MAJOR + 1;
318328
}

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ struct ModuleRebuildInfo {
316316
return "compiled with a newer version of the compiler";
317317
case Status::RevisionIncompatible:
318318
return "compiled with a different version of the compiler";
319+
case Status::ChannelIncompatible:
320+
return "compiled for a different distribution channel";
319321
case Status::NotInOSSA:
320322
return "module was not built with OSSA";
321323
case Status::NoncopyableGenericsMismatch:
@@ -1942,6 +1944,10 @@ InterfaceSubContextDelegateImpl::getCacheHash(StringRef useInterfacePath,
19421944
// that affects references serialized in the cached file.
19431945
sdkBuildVersion,
19441946

1947+
// Applying the distribution channel of the current compiler enables
1948+
// different compilers to share a module cache location.
1949+
version::getCurrentCompilerChannel(),
1950+
19451951
// Whether or not we're tracking system dependencies affects the
19461952
// invalidation behavior of this cache item.
19471953
genericSubInvocation.getFrontendOptions().shouldTrackSystemDependencies(),

lib/Serialization/ModuleFileSharedCore.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,21 @@ static ValidationInfo validateControlBlock(
418418
}
419419
break;
420420
}
421+
case control_block::CHANNEL: {
422+
static const char* ignoreRevision =
423+
::getenv("SWIFT_IGNORE_SWIFTMODULE_REVISION");
424+
if (ignoreRevision)
425+
break;
426+
427+
StringRef moduleChannel = blobData,
428+
compilerChannel = version::getCurrentCompilerChannel();
429+
if (requiresRevisionMatch && !compilerChannel.empty() &&
430+
moduleChannel != compilerChannel) {
431+
result.problematicChannel = moduleChannel;
432+
result.status = Status::ChannelIncompatible;
433+
}
434+
break;
435+
}
421436
case control_block::IS_OSSA: {
422437
auto isModuleInOSSA = scratch[0];
423438
if (requiresOSSAModules && !isModuleInOSSA)
@@ -532,6 +547,7 @@ std::string serialization::StatusToString(Status S) {
532547
case Status::FormatTooOld: return "FormatTooOld";
533548
case Status::FormatTooNew: return "FormatTooNew";
534549
case Status::RevisionIncompatible: return "RevisionIncompatible";
550+
case Status::ChannelIncompatible: return "ChannelIncompatible";
535551
case Status::NotInOSSA: return "NotInOSSA";
536552
case Status::NoncopyableGenericsMismatch:
537553
return "NoncopyableGenericsMismatch";

lib/Serialization/ModuleFormat.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 858; // Replace inherited types with protocols in protocol layout
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 859; // channel check
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///
@@ -860,6 +860,7 @@ namespace control_block {
860860
TARGET,
861861
SDK_NAME,
862862
REVISION,
863+
CHANNEL,
863864
IS_OSSA,
864865
ALLOWABLE_CLIENT_NAME,
865866
HAS_NONCOPYABLE_GENERICS,
@@ -898,6 +899,11 @@ namespace control_block {
898899
BCBlob
899900
>;
900901

902+
using ChannelLayout = BCRecordLayout<
903+
CHANNEL,
904+
BCBlob
905+
>;
906+
901907
using IsOSSALayout = BCRecordLayout<
902908
IS_OSSA,
903909
BCFixed<1>

lib/Serialization/Serialization.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,7 @@ void Serializer::writeBlockInfoBlock() {
834834
BLOCK_RECORD(control_block, TARGET);
835835
BLOCK_RECORD(control_block, SDK_NAME);
836836
BLOCK_RECORD(control_block, REVISION);
837+
BLOCK_RECORD(control_block, CHANNEL);
837838
BLOCK_RECORD(control_block, IS_OSSA);
838839
BLOCK_RECORD(control_block, ALLOWABLE_CLIENT_NAME);
839840
BLOCK_RECORD(control_block, HAS_NONCOPYABLE_GENERICS);
@@ -982,6 +983,7 @@ void Serializer::writeHeader() {
982983
control_block::TargetLayout Target(Out);
983984
control_block::SDKNameLayout SDKName(Out);
984985
control_block::RevisionLayout Revision(Out);
986+
control_block::ChannelLayout Channel(Out);
985987
control_block::IsOSSALayout IsOSSA(Out);
986988
control_block::AllowableClientLayout Allowable(Out);
987989
control_block::HasNoncopyableGenerics HasNoncopyableGenerics(Out);
@@ -1035,6 +1037,8 @@ void Serializer::writeHeader() {
10351037
forcedDebugRevision : version::getCurrentCompilerSerializationTag();
10361038
Revision.emit(ScratchRecord, revision);
10371039

1040+
Channel.emit(ScratchRecord, version::getCurrentCompilerChannel());
1041+
10381042
IsOSSA.emit(ScratchRecord, Options.IsOSSA);
10391043

10401044
HasNoncopyableGenerics.emit(ScratchRecord,

lib/Serialization/SerializedModuleLoader.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,12 @@ void swift::serialization::diagnoseSerializedASTLoadFailure(
10811081
Ctx.Diags.diagnose(diagLoc, diag::serialization_module_incompatible_revision,
10821082
loadInfo.problematicRevision, ModuleName, moduleBufferID);
10831083
break;
1084+
case serialization::Status::ChannelIncompatible:
1085+
Ctx.Diags.diagnose(diagLoc, diag::serialization_module_incompatible_channel,
1086+
loadInfo.problematicChannel,
1087+
version::getCurrentCompilerChannel(),
1088+
ModuleName, moduleBufferID);
1089+
break;
10841090
case serialization::Status::Malformed:
10851091
Ctx.Diags.diagnose(diagLoc, diag::serialization_malformed_module,
10861092
moduleBufferID);
@@ -1168,6 +1174,7 @@ void swift::serialization::diagnoseSerializedASTLoadFailureTransitive(
11681174
case serialization::Status::NotInOSSA:
11691175
case serialization::Status::NoncopyableGenericsMismatch:
11701176
case serialization::Status::RevisionIncompatible:
1177+
case serialization::Status::ChannelIncompatible:
11711178
case serialization::Status::Malformed:
11721179
case serialization::Status::MalformedDocumentation:
11731180
case serialization::Status::FailedToLoadBridgingHeader:
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Binary swiftmodules are restricted to a channel when set in the compiler.
2+
3+
// RUN: %empty-directory(%t/cache)
4+
// RUN: %empty-directory(%t/build)
5+
// RUN: split-file %s %t --leading-lines
6+
7+
//--- Lib.swift
8+
public func foo() {}
9+
10+
/// Build Lib as a resilient and non-resilient swiftmodule
11+
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -swift-version 5 \
12+
// RUN: -o %t/build -parse-stdlib -module-cache-path %t/cache \
13+
// RUN: -module-name ResilientLib -enable-library-evolution \
14+
// RUN: -emit-module-interface-path %t/build/ResilientLib.swiftinterface
15+
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -swift-version 5 \
16+
// RUN: -o %t/build -parse-stdlib -module-cache-path %t/cache \
17+
// RUN: -module-name NonResilientLib
18+
19+
/// Build a channel restricted Lib.
20+
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
21+
// RUN: %target-swift-frontend -emit-module %t/Lib.swift -swift-version 5 \
22+
// RUN: -o %t/build -parse-stdlib -module-cache-path %t/cache \
23+
// RUN: -module-name ChannelLib -enable-library-evolution
24+
25+
26+
/// 2. Test importing the non-resilient no-channel library from a channel compiler.
27+
//--- NonResilientClient.swift
28+
import NonResilientLib // expected-error {{compiled module was created for a different distribution channel '' than the local compiler 'restricted-channel', please ensure 'NonResilientLib' is found from the expected path:}}
29+
foo()
30+
31+
/// Building a NonResilientLib client should reject the import for a tagged compiler
32+
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
33+
// RUN: %target-swift-frontend -typecheck %t/NonResilientClient.swift \
34+
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache \
35+
// RUN: -verify -verify-ignore-unknown
36+
37+
38+
/// 3. Test importing the resilient no-channel library.
39+
//--- ResilientClient.swift
40+
import ResilientLib // expected-reject-error {{compiled module was created for a different distribution channel '' than the local compiler 'restricted-channel', please ensure 'ResilientLib' is found from the expected path:}}
41+
// expected-rebuild-remark @-1 {{rebuilding module 'ResilientLib' from interface}}
42+
// expected-rebuild-note @-2 {{compiled module is out of date}}
43+
// expected-rebuild-note @-3 {{compiled for a different distribution channel}}
44+
foo()
45+
46+
/// ResilientLib client should rebuild from swiftinterface when available.
47+
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
48+
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift \
49+
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache \
50+
// RUN: -verify -verify-additional-prefix rebuild- -Rmodule-interface-rebuild
51+
52+
/// Importing for a different channel should rebuild without cache collision.
53+
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=other-channel \
54+
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift \
55+
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache \
56+
// RUN: -verify -verify-additional-prefix rebuild- -Rmodule-interface-rebuild
57+
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
58+
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift \
59+
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache \
60+
// RUN: -verify -Rmodule-interface-rebuild
61+
62+
// RUN: rm %t/build/ResilientLib.swiftinterface
63+
// RUN: %empty-directory(%t/cache)
64+
65+
/// Building a ResilientLib client should succeed in no-channel / dev mode.
66+
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift \
67+
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
68+
69+
/// Building a ResilientLib client should reject the import for a channel compiler.
70+
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
71+
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift \
72+
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache \
73+
// RUN: -verify -verify-ignore-unknown -verify-additional-prefix reject-
74+
75+
/// Building a ResilientLib client should succeed for a channel compiler with SWIFT_IGNORE_SWIFTMODULE_REVISION
76+
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel SWIFT_IGNORE_SWIFTMODULE_REVISION=true \
77+
// RUN: %target-swift-frontend -typecheck %t/ResilientClient.swift \
78+
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
79+
80+
81+
/// 4. Test importing the channel restricted library
82+
//--- ChannelClient.swift
83+
import ChannelLib // expected-reject-error {{compiled module was created for a different distribution channel 'restricted-channel' than the local compiler 'other-channel', please ensure 'ChannelLib' is found from the expected path}}
84+
foo()
85+
86+
/// Importing ChannelLib should succeed with the same channel.
87+
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=restricted-channel \
88+
// RUN: %target-swift-frontend -typecheck %t/ChannelClient.swift \
89+
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
90+
91+
/// Importing ChannelLib should succeed with a dev compiler.
92+
// RUN: %target-swift-frontend -typecheck %t/ChannelClient.swift \
93+
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache
94+
95+
/// Importing ChannelLib from a different channel should be rejected.
96+
// RUN: env SWIFT_FORCE_SWIFTMODULE_CHANNEL=other-channel \
97+
// RUN: %target-swift-frontend -typecheck %t/ChannelClient.swift \
98+
// RUN: -swift-version 5 -I %t/build -parse-stdlib -module-cache-path %t/cache \
99+
// RUN: -verify -verify-ignore-unknown -verify-additional-prefix reject-

0 commit comments

Comments
 (0)