Skip to content

Commit 094e40e

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 094e40e

File tree

13 files changed

+96
-122
lines changed

13 files changed

+96
-122
lines changed

include/swift/AST/Decl.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2914,10 +2914,6 @@ class ValueDecl : public Decl {
29142914
/// if the base declaration is \c open, the override might have to be too.
29152915
bool hasOpenAccess(const DeclContext *useDC) const;
29162916

2917-
/// Returns whether this declaration should be treated as having the \c
2918-
/// package access level.
2919-
bool hasPackageAccess() const;
2920-
29212917
/// FIXME: This is deprecated.
29222918
bool isRecursiveValidation() const;
29232919

include/swift/AST/DeclContext.h

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

533+
/// True if resilience checks can be bypassed within a package.
534+
/// \p isForPackageDecl Bypassing only applies to package types
535+
/// (possibly also public types later) if opted-in, client and defining module
536+
/// are in the same package, and the defining module is a binary module.
537+
LLVM_READONLY
538+
bool allowBypassResilienceInPackage(bool isForPackageDecl) const;
539+
533540
/// Returns the module context that contains this context.
534541
LLVM_READONLY
535542
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: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2983,8 +2983,13 @@ bool AbstractStorageDecl::isFormallyResilient() const {
29832983
bool AbstractStorageDecl::isResilient() const {
29842984
if (!isFormallyResilient())
29852985
return false;
2986-
2987-
return getModuleContext()->isResilient();
2986+
if (!getModuleContext()->isResilient())
2987+
return false;
2988+
// Allows bypassing resilience checks for package decls
2989+
// at use site within a package if opted in, whether the
2990+
// loaded module was built resiliently or not.
2991+
return !getDeclContext()->allowBypassResilienceInPackage(getFormalAccessScope(/*useDC=*/nullptr,
2992+
/*treatUsableFromInlineAsPublic=*/true).isPackage());
29882993
}
29892994

29902995
bool AbstractStorageDecl::isResilient(ModuleDecl *M,
@@ -4219,13 +4224,6 @@ bool ValueDecl::hasOpenAccess(const DeclContext *useDC) const {
42194224
return access == AccessLevel::Open;
42204225
}
42214226

4222-
bool ValueDecl::hasPackageAccess() const {
4223-
AccessLevel access =
4224-
getAdjustedFormalAccess(this, /*useDC*/ nullptr,
4225-
/*treatUsableFromInlineAsPublic*/ false);
4226-
return access == AccessLevel::Package;
4227-
}
4228-
42294227
/// Given the formal access level for using \p VD, compute the scope where
42304228
/// \p VD may be accessed, taking \@usableFromInline, \@testable imports,
42314229
/// \@_spi imports, and enclosing access levels into account.
@@ -5063,11 +5061,17 @@ bool NominalTypeDecl::isFormallyResilient() const {
50635061
return true;
50645062
}
50655063

5064+
50665065
bool NominalTypeDecl::isResilient() const {
50675066
if (!isFormallyResilient())
50685067
return false;
5069-
5070-
return getModuleContext()->isResilient();
5068+
if (!getModuleContext()->isResilient())
5069+
return false;
5070+
// Allows bypassing resilience checks for package decls
5071+
// at use site within a package if opted in, whether the
5072+
// loaded module was built resiliently or not.
5073+
return !getDeclContext()->allowBypassResilienceInPackage(getFormalAccessScope(/*useDC=*/nullptr,
5074+
/*treatUsableFromInlineAsPublic=*/true).isPackage());
50715075
}
50725076

50735077
DestructorDecl *NominalTypeDecl::getValueTypeDestructor() {
@@ -6387,7 +6391,11 @@ bool EnumDecl::isFormallyExhaustive(const DeclContext *useDC) const {
63876391
// Non-public, non-versioned enums are always exhaustive.
63886392
AccessScope accessScope = getFormalAccessScope(/*useDC*/nullptr,
63896393
/*respectVersioned*/true);
6390-
if (!accessScope.isPublic())
6394+
// Both public and package enums should behave the same unless
6395+
// package enum is optimized with bypassing resilience checks.
6396+
if (!accessScope.isPublicOrPackage())
6397+
return true;
6398+
if (useDC && useDC->allowBypassResilienceInPackage(accessScope.isPackage()))
63916399
return true;
63926400

63936401
// All other checks are use-site specific; with no further information, the

lib/AST/DeclContext.cpp

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

303+
bool DeclContext::allowBypassResilienceInPackage(bool isForPackageDecl) const {
304+
// Bypassing resilience checks only applies to package types.
305+
if (!isForPackageDecl)
306+
return false;
307+
308+
auto shouldAllow = true;
309+
// Check if the enclosing type is non-resilient.
310+
if (auto enclosingNominal = dyn_cast<NominalTypeDecl>(this)) {
311+
shouldAllow = !enclosingNominal->isResilient();
312+
} else if (auto enclosingExt = dyn_cast<ExtensionDecl>(this)) {
313+
if (auto extNominal = enclosingExt->getExtendedNominal())
314+
shouldAllow = !extNominal->isResilient();
315+
}
316+
317+
// Check if opted-in for bypassing resilience checks, client and defining
318+
// module are in the same package, and defining module is a binary module.
319+
return shouldAllow &&
320+
getASTContext().LangOpts.EnableBypassResilienceInPackage &&
321+
getParentModule()->inPackage(getASTContext().LangOpts.PackageName) &&
322+
!getParentModule()->isBuiltFromInterface();
323+
}
324+
303325
ModuleDecl *DeclContext::getParentModule() const {
304326
// If the current context is PackageUnit, return the module
305327
// decl context pointing to the current context. This check

lib/AST/ProtocolConformance.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,7 @@ bool NormalProtocolConformance::isResilient() const {
348348
// individual witnesses.
349349
if (!getDeclContext()->getSelfNominalTypeDecl()->isResilient())
350350
return false;
351-
352-
return getDeclContext()->getParentModule()->isResilient();
351+
return !getDeclContext()->getParentModule()->isResilient();
353352
}
354353

355354
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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,8 @@ 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 = Args.hasArg(OPT_package_bypass_resilience_optimization);
635+
634636
Opts.DisableAvailabilityChecking |=
635637
Args.hasArg(OPT_disable_availability_checking);
636638
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)