Skip to content

Commit 098f2a9

Browse files
authored
Merge pull request #79157 from swiftlang/elsh/pcmo-bypass-res-check-mods
Package CMO: Skip deserialization error checks for same-module decls.
2 parents 0c7c79d + ee75183 commit 098f2a9

File tree

7 files changed

+50
-36
lines changed

7 files changed

+50
-36
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4745,7 +4745,10 @@ NOTE(ambiguous_because_of_trailing_closure,none,
47454745
(bool, const ValueDecl *))
47464746

47474747
// In-package resilience bypassing
4748-
WARNING(cannot_bypass_resilience_due_to_missing_member,none,
4748+
WARNING(cannot_bypass_resilience_due_to_missing_member_warn,none,
4749+
"cannot bypass resilience due to member deserialization failure while attempting to access %select{member %0|missing member}1 of %2 in module %3 from module %4",
4750+
(Identifier, bool, Identifier, Identifier, Identifier))
4751+
ERROR(cannot_bypass_resilience_due_to_missing_member,none,
47494752
"cannot bypass resilience due to member deserialization failure while attempting to access %select{member %0|missing member}1 of %2 in module %3 from module %4",
47504753
(Identifier, bool, Identifier, Identifier, Identifier))
47514754

include/swift/Basic/LangOptions.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -603,12 +603,11 @@ namespace swift {
603603
/// from source.
604604
bool AllowNonResilientAccess = false;
605605

606-
/// When Package CMO is enabled, deserialization checks are done to
607-
/// ensure that the members of a decl are correctly deserialized to maintain
608-
/// proper layout. This ensures that bypassing resilience is safe. Accessing
609-
/// an incorrectly laid-out decl directly can lead to runtime crashes. This flag
610-
/// should only be used temporarily during migration to enable Package CMO.
611-
bool SkipDeserializationChecksForPackageCMO = false;
606+
/// When Package CMO is enabled, deserialization checks ensure that a decl's
607+
/// members are correctly deserialized to maintain the proper layout—a prerequisite
608+
/// for bypassing resilience when accessing the decl. By default, a warning is issued
609+
/// if a deserialization failure is found; this flag causes the build to fail fast instead.
610+
bool AbortOnDeserializationFailForPackageCMO = false;
612611

613612
/// Enables dumping type witness systems from associated type inference.
614613
bool DumpTypeWitnessSystems = false;

include/swift/Option/Options.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,9 +1139,9 @@ def Oplayground : Flag<["-"], "Oplayground">, Group<O_Group>,
11391139
Flags<[HelpHidden, FrontendOption, ModuleInterfaceOption]>,
11401140
HelpText<"Compile with optimizations appropriate for a playground">;
11411141

1142-
def ExperimentalSkipDeserializationChecksForPackageCMO : Flag<["-"], "experimental-skip-deserialization-checks-for-package-cmo">,
1142+
def ExperimentalPackageCMOAbortOnDeserializationFail : Flag<["-"], "experimental-package-cmo-abort-on-deserialization-fail">,
11431143
Flags<[FrontendOption]>,
1144-
HelpText<"Skip deserialization checks for package-cmo; use only for experimental purposes">;
1144+
HelpText<"Abort if a deserialization error is found while package optimization is enabled">;
11451145

11461146
def ExperimentalPackageCMO : Flag<["-"], "experimental-package-cmo">,
11471147
Flags<[FrontendOption]>,

lib/AST/Decl.cpp

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4811,20 +4811,10 @@ bool ValueDecl::bypassResilienceInPackage(ModuleDecl *accessingModule) const {
48114811
if (declModule->isResilient() &&
48124812
declModule->allowNonResilientAccess() &&
48134813
declModule->serializePackageEnabled()) {
4814-
// Fail and diagnose if there is a member deserialization error,
4815-
// with an option to skip for temporary/migration purposes.
4816-
if (!getASTContext().LangOpts.SkipDeserializationChecksForPackageCMO) {
4817-
// Since we're checking for deserialization errors, make sure the
4818-
// accessing module is different from this decl's module.
4819-
if (accessingModule &&
4820-
accessingModule != declModule) {
4821-
if (auto IDC = dyn_cast<IterableDeclContext>(this)) {
4822-
// Recursively check if members and their members have failing
4823-
// deserialization, and emit a diagnostic.
4824-
// FIXME: It throws a warning for now; need to upgrade to an error.
4825-
IDC->checkDeserializeMemberErrorInPackage(accessingModule);
4826-
}
4827-
}
4814+
if (auto IDC = dyn_cast<IterableDeclContext>(this)) {
4815+
// Recursively check if this decl's members and their members have
4816+
// been deserialized correctly, and emit a diagnostic if not.
4817+
IDC->checkDeserializeMemberErrorInPackage(accessingModule);
48284818
}
48294819
return true;
48304820
}

lib/AST/DeclContext.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,12 +1180,15 @@ void IterableDeclContext::loadAllMembers() const {
11801180
--s->getFrontendCounters().NumUnloadedLazyIterableDeclContexts;
11811181
}
11821182

1183-
// Checks whether members of this decl and their respective members
1184-
// (recursively) were deserialized correctly and emits a diagnostic
1185-
// if deserialization failed. Requires accessing module and this decl's
1186-
// module are in the same package, and this decl's module has package
1187-
// optimization enabled.
1183+
// Checks whether members of this decl and their respective member decls
1184+
// (recursively) were deserialized into another module correctly, and
1185+
// emits a diagnostic if deserialization failed. Requires the other module
1186+
// module and this decl's module are in the same package, and this
1187+
// decl's module has package optimization enabled.
11881188
void IterableDeclContext::checkDeserializeMemberErrorInPackage(ModuleDecl *accessingModule) {
1189+
// For decls in the same module, we can skip deserialization error checks.
1190+
if (getDecl()->getModuleContext() == accessingModule)
1191+
return;
11891192
// Only check if accessing module is in the same package as this
11901193
// decl's module, which has package optimization enabled.
11911194
if (!getDecl()->getModuleContext()->inSamePackage(accessingModule) ||
@@ -1220,6 +1223,10 @@ void IterableDeclContext::checkDeserializeMemberErrorInPackage(ModuleDecl *acces
12201223
containerID = container->getBaseIdentifier();
12211224
}
12221225

1226+
auto diagID = diag::cannot_bypass_resilience_due_to_missing_member_warn;
1227+
if (getASTContext().LangOpts.AbortOnDeserializationFailForPackageCMO)
1228+
diagID = diag::cannot_bypass_resilience_due_to_missing_member;
1229+
12231230
auto foundMissing = false;
12241231
for (auto member: memberList) {
12251232
// Only storage vars can affect memory layout so
@@ -1237,7 +1244,7 @@ void IterableDeclContext::checkDeserializeMemberErrorInPackage(ModuleDecl *acces
12371244
foundMissing = false;
12381245
auto missingMemberID = missingMember->getName().getBaseIdentifier();
12391246
getASTContext().Diags.diagnose(member->getLoc(),
1240-
diag::cannot_bypass_resilience_due_to_missing_member,
1247+
diagID,
12411248
missingMemberID,
12421249
missingMemberID.empty(),
12431250
containerID,
@@ -1249,7 +1256,7 @@ void IterableDeclContext::checkDeserializeMemberErrorInPackage(ModuleDecl *acces
12491256
// If not handled above, emit a diag here.
12501257
if (foundMissing) {
12511258
getASTContext().Diags.diagnose(getDecl()->getLoc(),
1252-
diag::cannot_bypass_resilience_due_to_missing_member,
1259+
diagID,
12531260
Identifier(),
12541261
true,
12551262
containerID,

lib/Frontend/CompilerInvocation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1349,7 +1349,7 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
13491349
}
13501350
}
13511351

1352-
Opts.SkipDeserializationChecksForPackageCMO = Args.hasArg(OPT_ExperimentalSkipDeserializationChecksForPackageCMO);
1352+
Opts.AbortOnDeserializationFailForPackageCMO = Args.hasArg(OPT_ExperimentalPackageCMOAbortOnDeserializationFail);
13531353
Opts.AllowNonResilientAccess =
13541354
Args.hasArg(OPT_experimental_allow_non_resilient_access) ||
13551355
Args.hasArg(OPT_allow_non_resilient_access) ||

test/SILOptimizer/package-cmo-disallow-bypass-resilience-on-deserialization-fail.swift

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@
2020
// RUN: -I %t/artifacts/ObjcBuilds -L %t/artifacts/ObjcBuilds \
2121
// RUN: -lObjCAPI -Xcc -DINCLUDE_FOO
2222

23+
/// Build a swift module that depends on the above Core swift module without `INCLUDE_FOO`;
24+
/// it should fail and diagnose that there was a deserialization failure.
25+
// RUN: not %target-build-swift-dylib(%t/artifacts/SwiftBuilds/%target-library-name(MyUIA)) %t/src/UIA.swift \
26+
// RUN: -module-name MyUIA -emit-module -package-name pkg \
27+
// RUN: -enable-library-evolution -O -wmo \
28+
// RUN: -Xfrontend -experimental-package-cmo-abort-on-deserialization-fail \
29+
// RUN: -I %t/artifacts/SwiftBuilds -L %t/artifacts/SwiftBuilds \
30+
// RUN: -I %t/artifacts/ObjcBuilds -L %t/artifacts/ObjcBuilds \
31+
// RUN: -lMyCore -lObjCAPI -Rmodule-loading 2>&1 | tee %t/MyUIA-build.txt
32+
// RUN: %FileCheck %s --check-prefix=CHECK-ERROR < %t/MyUIA-build.txt
33+
// CHECK-ERROR: error: cannot bypass resilience due to member deserialization failure while attempting to access missing member of 'PkgStructA' in module 'MyCore' from module 'MyUIA'
34+
2335
/// Build another swift module that depends on Core; since FooObjc is not referenced
2436
/// in the call chain, there's no deserialization failure, and bypassing resilience is allowed.
2537
// RUN: %target-build-swift-dylib(%t/artifacts/SwiftBuilds/%target-library-name(MyUIB)) %t/src/UIB.swift \
@@ -35,10 +47,12 @@
3547

3648
//--- UIA.swift
3749
package import MyCore
38-
package import ObjCAPI
50+
import ObjCAPI
3951

4052
package func testWrapperA(_ arg: WrapperA) {
41-
print(arg.varA, arg.varAdict, arg.varAarray)
53+
if let x = arg.varA, let y = arg.varAdict, let z = arg.varAarray {
54+
print(x, y, z)
55+
}
4256
}
4357

4458
package func testKlass(_ arg: Klass) {
@@ -47,11 +61,12 @@ package func testKlass(_ arg: Klass) {
4761

4862
//--- UIB.swift
4963
public import MyCore
50-
public import ObjCAPI
64+
import ObjCAPI
5165

5266
package func testWrapperB(_ arg: WrapperB) {
53-
let v = arg.varB
54-
print(v)
67+
if let v = arg.varB {
68+
print(v)
69+
}
5570
}
5671

5772
package func testKlass(_ arg: Klass) {

0 commit comments

Comments
 (0)