Skip to content

Commit f72a3af

Browse files
committed
Support bypassing resilience checks for package decls at use site in a package.
By default package decls are treated as resilient, similar to public (non-frozen). This PR adds support to allow direct access to package decls at use site if opted-in. Requires the loaded module to be a binary module in the same package. Resolves rdar://121626315
1 parent dc959a4 commit f72a3af

File tree

12 files changed

+87
-16
lines changed

12 files changed

+87
-16
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4097,6 +4097,8 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
40974097
/// type from the given module?
40984098
bool isResilient(ModuleDecl *M, ResilienceExpansion expansion) const;
40994099

4100+
bool allowBypassResilienceInPackage() const;
4101+
41004102
/// Determine whether we have already attempted to add any
41014103
/// implicitly-defined initializers to this declaration.
41024104
bool addedImplicitInitializers() const {
@@ -5852,6 +5854,8 @@ class AbstractStorageDecl : public ValueDecl {
58525854
/// property from the given module?
58535855
bool isResilient(ModuleDecl *M, ResilienceExpansion expansion) const;
58545856

5857+
bool allowBypassResilienceInPackage() const;
5858+
58555859
/// True if the storage can be referenced by a keypath directly.
58565860
/// Otherwise, its override must be referenced.
58575861
bool isValidKeyPathComponent() const;

include/swift/AST/DeclContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,9 @@ class alignas(1 << DeclContextAlignInBits) DeclContext
530530
LLVM_READONLY
531531
PackageUnit *getPackageContext(bool lookupIfNotCurrent = false) const;
532532

533+
LLVM_READONLY
534+
bool allowBypassResilienceInPackage() const;
535+
533536
/// Returns the module context that contains this context.
534537
LLVM_READONLY
535538
ModuleDecl *getParentModule() const;

include/swift/AST/Module.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,10 @@ class ModuleDecl
480480
return Identifier();
481481
}
482482

483+
bool inPackage(std::string packageName) {
484+
return !getPackageName().empty() && getPackageName().str() == packageName;
485+
}
486+
483487
/// Get the package associated with this module
484488
PackageUnit *getPackage() const { return Package; }
485489

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ namespace swift {
186186
/// Disable API availability checking.
187187
bool DisableAvailabilityChecking = false;
188188

189+
/// Enable optimization to bypass resilience checks in a package
190+
bool EnableBypassResilienceInPackage = false;
191+
189192
/// Optimization mode for unavailable declarations.
190193
llvm::Optional<UnavailableDeclOptimization> UnavailableDeclOptimizationMode;
191194

include/swift/Option/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,10 @@ def unavailable_decl_optimization_EQ : Joined<["-"], "unavailable-decl-optimizat
516516
"value may be 'none' (no optimization) or 'complete' (code is not "
517517
"generated at all unavailable declarations)">;
518518

519+
def package_bypass_resilience_optimization : Flag<["-"], "package-bypass-resilience-optimization">,
520+
Flags<[FrontendOption]>,
521+
HelpText<"Enable optimization to bypass resilience within a package">;
522+
519523
def library_level : Separate<["-"], "library-level">,
520524
MetaVarName<"<level>">,
521525
Flags<[HelpHidden, FrontendOption, ModuleInterfaceOption]>,

lib/AST/Decl.cpp

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2980,11 +2980,32 @@ bool AbstractStorageDecl::isFormallyResilient() const {
29802980
return true;
29812981
}
29822982

2983+
bool AbstractStorageDecl::allowBypassResilienceInPackage() const {
2984+
auto shouldAllow = true;
2985+
// Only allow bypassing resilience checks for package decls
2986+
// if the enclosing decl's non-resilient
2987+
if (auto enclosingNominal = dyn_cast<NominalTypeDecl>(getDeclContext())) {
2988+
shouldAllow = !enclosingNominal->isResilient();
2989+
} else if (auto enclosingExt = dyn_cast<ExtensionDecl>(getDeclContext())) {
2990+
if (auto nominal = enclosingExt->getExtendedNominal())
2991+
shouldAllow = !nominal->isResilient();
2992+
}
2993+
if (shouldAllow &&
2994+
getFormalAccessScope(/*useDC=*/nullptr,
2995+
/*treatUsableFromInlineAsPublic=*/true).isPackage())
2996+
return getModuleContext()->allowBypassResilienceInPackage();
2997+
return false;
2998+
}
2999+
29833000
bool AbstractStorageDecl::isResilient() const {
29843001
if (!isFormallyResilient())
29853002
return false;
2986-
2987-
return getModuleContext()->isResilient();
3003+
if (!getModuleContext()->isResilient())
3004+
return false;
3005+
// Allows bypassing resilience checks for package
3006+
// decls within a package whether the loaded module
3007+
// was built resiliently or not.
3008+
return !allowBypassResilienceInPackage();
29883009
}
29893010

29903011
bool AbstractStorageDecl::isResilient(ModuleDecl *M,
@@ -5063,11 +5084,32 @@ bool NominalTypeDecl::isFormallyResilient() const {
50635084
return true;
50645085
}
50655086

5087+
bool NominalTypeDecl::allowBypassResilienceInPackage() const {
5088+
auto shouldAllow = true;
5089+
// Only allow bypassing resilience checks for package decls
5090+
// if the enclosing decl's non-resilient
5091+
if (auto enclosingNominal = dyn_cast<NominalTypeDecl>(getDeclContext())) {
5092+
shouldAllow = !enclosingNominal->isResilient();
5093+
} else if (auto enclosingExt = dyn_cast<ExtensionDecl>(getDeclContext())) {
5094+
if (auto nominal = enclosingExt->getExtendedNominal())
5095+
shouldAllow = !nominal->isResilient();
5096+
}
5097+
if (shouldAllow &&
5098+
getFormalAccessScope(/*useDC=*/nullptr,
5099+
/*treatUsableFromInlineAsPublic=*/true).isPackage())
5100+
return getModuleContext()->allowBypassResilienceInPackage();
5101+
return false;
5102+
}
5103+
50665104
bool NominalTypeDecl::isResilient() const {
50675105
if (!isFormallyResilient())
50685106
return false;
5069-
5070-
return getModuleContext()->isResilient();
5107+
if (!getModuleContext()->isResilient())
5108+
return false;
5109+
// Allows bypassing resilience checks for package
5110+
// decls within a package whether the loaded module
5111+
// was built resiliently or not.
5112+
return !allowBypassResilienceInPackage();
50715113
}
50725114

50735115
DestructorDecl *NominalTypeDecl::getValueTypeDestructor() {

lib/AST/DeclContext.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,12 @@ PackageUnit *DeclContext::getPackageContext(bool lookupIfNotCurrent) const {
300300
return nullptr;
301301
}
302302

303+
bool DeclContext::allowBypassResilienceInPackage() const {
304+
return getASTContext().LangOpts.EnableBypassResilienceInPackage &&
305+
getParentModule()->inPackage(getASTContext().LangOpts.PackageName) &&
306+
!getParentModule()->isBuiltFromInterface();
307+
}
308+
303309
ModuleDecl *DeclContext::getParentModule() const {
304310
// If the current context is PackageUnit, return the module
305311
// decl context pointing to the current context. This check

lib/AST/ProtocolConformance.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,12 @@ bool NormalProtocolConformance::isResilient() const {
348348
// individual witnesses.
349349
if (!getDeclContext()->getSelfNominalTypeDecl()->isResilient())
350350
return false;
351-
352-
return getDeclContext()->getParentModule()->isResilient();
351+
if (!getDeclContext()->getParentModule()->isResilient())
352+
return false;
353+
// Allows bypassing resilience checks for package
354+
// decls within a package whether the loaded module
355+
// was built resiliently or not.
356+
return !getDeclContext()->getSelfNominalTypeDecl()->allowBypassResilienceInPackage();
353357
}
354358

355359
llvm::Optional<ArrayRef<Requirement>>

lib/Driver/ToolChains.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
299299
inputArgs.AddLastArg(arguments, options::OPT_Rpass_missed_EQ);
300300
inputArgs.AddLastArg(arguments, options::OPT_suppress_warnings);
301301
inputArgs.AddLastArg(arguments, options::OPT_suppress_remarks);
302+
inputArgs.AddLastArg(arguments, options::OPT_package_bypass_resilience_optimization);
302303
inputArgs.AddLastArg(arguments, options::OPT_profile_generate);
303304
inputArgs.AddLastArg(arguments, options::OPT_profile_use);
304305
inputArgs.AddLastArg(arguments, options::OPT_profile_coverage_mapping);
@@ -551,7 +552,7 @@ ToolChain::constructInvocation(const CompileJobAction &job,
551552
if (context.Args.hasArg(options::OPT_CrossModuleOptimization)) {
552553
Arguments.push_back("-cross-module-optimization");
553554
}
554-
555+
555556
if (context.Args.hasArg(options::OPT_ExperimentalPerformanceAnnotations)) {
556557
Arguments.push_back("-experimental-performance-annotations");
557558
}

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
631631
Opts.EnablePackageInterfaceLoad = Args.hasArg(OPT_experimental_package_interface_load) ||
632632
::getenv("SWIFT_ENABLE_PACKAGE_INTERFACE_LOAD");
633633

634+
Opts.EnableBypassResilienceInPackage =
635+
Args.hasArg(OPT_package_bypass_resilience_optimization);
636+
634637
Opts.DisableAvailabilityChecking |=
635638
Args.hasArg(OPT_disable_availability_checking);
636639
if (Args.hasArg(OPT_check_api_availability_only))

lib/Sema/ImportResolution.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -762,8 +762,7 @@ void UnboundImport::validateInterfaceWithPackageName(ModuleDecl *topLevelModule,
762762

763763
// If source file is .swift or non-interface, show diags when importing an interface file
764764
ASTContext &ctx = topLevelModule->getASTContext();
765-
if (!topLevelModule->getPackageName().empty() &&
766-
topLevelModule->getPackageName().str() == ctx.LangOpts.PackageName &&
765+
if (topLevelModule->inPackage(ctx.LangOpts.PackageName) &&
767766
topLevelModule->isBuiltFromInterface() &&
768767
!topLevelModule->getModuleSourceFilename().endswith(".package.swiftinterface")) {
769768
ctx.Diags.diagnose(import.module.getModulePath().front().Loc,

test/IRGen/package_resilience.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
//
22
// Unlike its counterparts in the other *_resilience.swift files, the goal is
33
// for the package's component modules to all be considered within the same
4-
// resilience domain. This file ensures that we use direct access as much as
5-
// possible.
4+
// resilience domain. This file ensures that we use direct access to package
5+
// decls at use site as much as possible with -package-bypass-resilience-optimization.
66
//
77

8-
// REQUIRES: rdar118947451
9-
108
// RUN: %empty-directory(%t)
119
// RUN: %{python} %utils/chex.py < %s > %t/package_resilience.swift
1210
// RUN: %target-swift-frontend -package-name MyPkg -emit-module -enable-library-evolution -emit-module-path=%t/resilient_struct.swiftmodule -module-name=resilient_struct %S/Inputs/package_types/resilient_struct.swift
1311
// RUN: %target-swift-frontend -package-name MyPkg -emit-module -enable-library-evolution -emit-module-path=%t/resilient_enum.swiftmodule -module-name=resilient_enum -I %t %S/Inputs/package_types/resilient_enum.swift
1412
// RUN: %target-swift-frontend -package-name MyPkg -emit-module -enable-library-evolution -emit-module-path=%t/resilient_class.swiftmodule -module-name=resilient_class -I %t %S/Inputs/package_types/resilient_class.swift
15-
// RUN: %target-swift-frontend -package-name MyPkg -enable-objc-interop -I %t -emit-ir -enable-library-evolution %t/package_resilience.swift | %FileCheck %t/package_resilience.swift --check-prefixes=CHECK,CHECK-objc,CHECK-objc%target-ptrsize,CHECK-%target-ptrsize,CHECK-%target-cpu,CHECK-%target-import-type-objc-STABLE-ABI-%target-mandates-stable-abi,CHECK-%target-sdk-name -DINT=i%target-ptrsize -D#MDWORDS=7 -D#MDSIZE32=52 -D#MDSIZE64=80 -D#WORDSIZE=%target-alignment
16-
// RUN: %target-swift-frontend -package-name MyPkg -disable-objc-interop -I %t -emit-ir -enable-library-evolution %t/package_resilience.swift | %FileCheck %t/package_resilience.swift --check-prefixes=CHECK,CHECK-native,CHECK-native%target-ptrsize,CHECK-%target-ptrsize,CHECK-%target-cpu,CHECK-native-STABLE-ABI-%target-mandates-stable-abi,CHECK-%target-sdk-name -DINT=i%target-ptrsize -D#MDWORDS=4 -D#MDSIZE32=40 -D#MDSIZE64=56 -D#WORDSIZE=%target-alignment
17-
// RUN: %target-swift-frontend -package-name MyPkg -I %t -emit-ir -enable-library-evolution -O %t/package_resilience.swift -package-name MyPkg
13+
// RUN: %target-swift-frontend -package-name MyPkg -package-bypass-resilience-optimization -enable-objc-interop -I %t -emit-ir -enable-library-evolution %t/package_resilience.swift | %FileCheck %t/package_resilience.swift --check-prefixes=CHECK,CHECK-objc,CHECK-objc%target-ptrsize,CHECK-%target-ptrsize,CHECK-%target-cpu,CHECK-%target-import-type-objc-STABLE-ABI-%target-mandates-stable-abi,CHECK-%target-sdk-name -DINT=i%target-ptrsize -D#MDWORDS=7 -D#MDSIZE32=52 -D#MDSIZE64=80 -D#WORDSIZE=%target-alignment
14+
// RUN: %target-swift-frontend -package-name MyPkg -package-bypass-resilience-optimization -disable-objc-interop -I %t -emit-ir -enable-library-evolution %t/package_resilience.swift | %FileCheck %t/package_resilience.swift --check-prefixes=CHECK,CHECK-native,CHECK-native%target-ptrsize,CHECK-%target-ptrsize,CHECK-%target-cpu,CHECK-native-STABLE-ABI-%target-mandates-stable-abi,CHECK-%target-sdk-name -DINT=i%target-ptrsize -D#MDWORDS=4 -D#MDSIZE32=40 -D#MDSIZE64=56 -D#WORDSIZE=%target-alignment
15+
// RUN: %target-swift-frontend -package-name MyPkg -package-bypass-resilience-optimization -I %t -emit-ir -enable-library-evolution -O %t/package_resilience.swift -package-name MyPkg
1816
// REQUIRES: objc_codegen
1917
// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos
2018
// REQUIRES: CPU=x86_64 || CPU=arm64

0 commit comments

Comments
 (0)