Skip to content

Commit 4588cc2

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 4112618 commit 4588cc2

File tree

13 files changed

+94
-121
lines changed

13 files changed

+94
-121
lines changed

include/swift/AST/Decl.h

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

2923-
/// Returns whether this declaration should be treated as having the \c
2924-
/// package access level.
2925-
bool hasPackageAccess() const;
2926-
29272923
/// FIXME: This is deprecated.
29282924
bool isRecursiveValidation() const;
29292925

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: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2975,8 +2975,13 @@ bool AbstractStorageDecl::isFormallyResilient() const {
29752975
bool AbstractStorageDecl::isResilient() const {
29762976
if (!isFormallyResilient())
29772977
return false;
2978-
2979-
return getModuleContext()->isResilient();
2978+
if (!getModuleContext()->isResilient())
2979+
return false;
2980+
// Allows bypassing resilience checks for package decls
2981+
// at use site within a package if opted in, whether the
2982+
// loaded module was built resiliently or not.
2983+
return !getDeclContext()->allowBypassResilienceInPackage(getFormalAccessScope(/*useDC=*/nullptr,
2984+
/*treatUsableFromInlineAsPublic=*/true).isPackage());
29802985
}
29812986

29822987
bool AbstractStorageDecl::isResilient(ModuleDecl *M,
@@ -4207,13 +4212,6 @@ bool ValueDecl::hasOpenAccess(const DeclContext *useDC) const {
42074212
return access == AccessLevel::Open;
42084213
}
42094214

4210-
bool ValueDecl::hasPackageAccess() const {
4211-
AccessLevel access =
4212-
getAdjustedFormalAccess(this, /*useDC*/ nullptr,
4213-
/*treatUsableFromInlineAsPublic*/ false);
4214-
return access == AccessLevel::Package;
4215-
}
4216-
42174215
/// Given the formal access level for using \p VD, compute the scope where
42184216
/// \p VD may be accessed, taking \@usableFromInline, \@testable imports,
42194217
/// \@_spi imports, and enclosing access levels into account.
@@ -5054,8 +5052,13 @@ bool NominalTypeDecl::isFormallyResilient() const {
50545052
bool NominalTypeDecl::isResilient() const {
50555053
if (!isFormallyResilient())
50565054
return false;
5057-
5058-
return getModuleContext()->isResilient();
5055+
if (!getModuleContext()->isResilient())
5056+
return false;
5057+
// Allows bypassing resilience checks for package decls
5058+
// at use site within a package if opted in, whether the
5059+
// loaded module was built resiliently or not.
5060+
return !getDeclContext()->allowBypassResilienceInPackage(getFormalAccessScope(/*useDC=*/nullptr,
5061+
/*treatUsableFromInlineAsPublic=*/true).isPackage());
50595062
}
50605063

50615064
DestructorDecl *NominalTypeDecl::getValueTypeDestructor() {
@@ -6375,7 +6378,11 @@ bool EnumDecl::isFormallyExhaustive(const DeclContext *useDC) const {
63756378
// Non-public, non-versioned enums are always exhaustive.
63766379
AccessScope accessScope = getFormalAccessScope(/*useDC*/nullptr,
63776380
/*respectVersioned*/true);
6378-
if (!accessScope.isPublic())
6381+
// Both public and package enums should behave the same unless
6382+
// package enum is optimized with bypassing resilience checks.
6383+
if (!accessScope.isPublicOrPackage())
6384+
return true;
6385+
if (useDC && useDC->allowBypassResilienceInPackage(accessScope.isPackage()))
63796386
return true;
63806387

63816388
// 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: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,6 @@ bool NormalProtocolConformance::isResilient() const {
349349
// individual witnesses.
350350
if (!getDeclContext()->getSelfNominalTypeDecl()->isResilient())
351351
return false;
352-
353352
return getDeclContext()->getParentModule()->isResilient();
354353
}
355354

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
@@ -671,6 +671,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
671671
Opts.EnablePackageInterfaceLoad = Args.hasArg(OPT_experimental_package_interface_load) ||
672672
::getenv("SWIFT_ENABLE_PACKAGE_INTERFACE_LOAD");
673673

674+
Opts.EnableBypassResilienceInPackage = Args.hasArg(OPT_package_bypass_resilience_optimization);
675+
674676
Opts.DisableAvailabilityChecking |=
675677
Args.hasArg(OPT_disable_availability_checking);
676678
if (Args.hasArg(OPT_check_api_availability_only))

0 commit comments

Comments
 (0)