Skip to content

Commit 633051e

Browse files
authored
Merge pull request #71430 from apple/es-pkg-bypass
Support bypassing resilience checks for package decls at use site in a package
2 parents 5ce824f + 7d4e567 commit 633051e

File tree

13 files changed

+111
-151
lines changed

13 files changed

+111
-151
lines changed

include/swift/AST/Decl.h

Lines changed: 0 additions & 11 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

@@ -5846,13 +5842,6 @@ class AbstractStorageDecl : public ValueDecl {
58465842
ModuleDecl *module,
58475843
ResilienceExpansion expansion) const;
58485844

5849-
/// Should this declaration behave as if it must be accessed
5850-
/// resiliently, even when we're building a non-resilient module?
5851-
///
5852-
/// This is used for diagnostics, because we do not want a behavior
5853-
/// change between builds with resilience enabled and disabled.
5854-
bool isFormallyResilient() const;
5855-
58565845
/// Do we need to use resilient access patterns outside of this
58575846
/// property's resilience domain?
58585847
bool isResilient() const;

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 bypassResilienceInPackage(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 inSamePackage(ModuleDecl *other) {
484+
return !getPackageName().empty() && getPackageName() == other->getPackageName();
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: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2952,31 +2952,30 @@ bool Decl::isOutermostPrivateOrFilePrivateScope() const {
29522952
!isInPrivateOrLocalContext(this);
29532953
}
29542954

2955-
bool AbstractStorageDecl::isFormallyResilient() const {
2955+
bool AbstractStorageDecl::isResilient() const {
29562956
// Check for an explicit @_fixed_layout attribute.
29572957
if (getAttrs().hasAttribute<FixedLayoutAttr>())
29582958
return false;
29592959

29602960
// If we're an instance property of a nominal type, query the type.
2961-
auto *dc = getDeclContext();
29622961
if (!isStatic())
2963-
if (auto *nominalDecl = dc->getSelfNominalTypeDecl())
2962+
if (auto *nominalDecl = getDeclContext()->getSelfNominalTypeDecl())
29642963
return nominalDecl->isResilient();
29652964

29662965
// Non-public global and static variables always have a
29672966
// fixed layout.
2968-
if (!getFormalAccessScope(/*useDC=*/nullptr,
2969-
/*treatUsableFromInlineAsPublic=*/true).isPublicOrPackage())
2967+
auto accessScope = getFormalAccessScope(/*useDC=*/nullptr,
2968+
/*treatUsableFromInlineAsPublic=*/true);
2969+
if (!accessScope.isPublicOrPackage())
29702970
return false;
29712971

2972-
return true;
2973-
}
2974-
2975-
bool AbstractStorageDecl::isResilient() const {
2976-
if (!isFormallyResilient())
2972+
if (!getModuleContext()->isResilient())
29772973
return false;
29782974

2979-
return getModuleContext()->isResilient();
2975+
// Allows bypassing resilience checks for package decls
2976+
// at use site within a package if opted in, whether the
2977+
// loaded module was built resiliently or not.
2978+
return !getDeclContext()->bypassResilienceInPackage(accessScope.isPackage());
29802979
}
29812980

29822981
bool AbstractStorageDecl::isResilient(ModuleDecl *M,
@@ -4207,13 +4206,6 @@ bool ValueDecl::hasOpenAccess(const DeclContext *useDC) const {
42074206
return access == AccessLevel::Open;
42084207
}
42094208

4210-
bool ValueDecl::hasPackageAccess() const {
4211-
AccessLevel access =
4212-
getAdjustedFormalAccess(this, /*useDC*/ nullptr,
4213-
/*treatUsableFromInlineAsPublic*/ false);
4214-
return access == AccessLevel::Package;
4215-
}
4216-
42174209
/// Given the formal access level for using \p VD, compute the scope where
42184210
/// \p VD may be accessed, taking \@usableFromInline, \@testable imports,
42194211
/// \@_spi imports, and enclosing access levels into account.
@@ -5054,8 +5046,14 @@ bool NominalTypeDecl::isFormallyResilient() const {
50545046
bool NominalTypeDecl::isResilient() const {
50555047
if (!isFormallyResilient())
50565048
return false;
5057-
5058-
return getModuleContext()->isResilient();
5049+
if (!getModuleContext()->isResilient())
5050+
return false;
5051+
// Allows bypassing resilience checks for package decls
5052+
// at use site within a package if opted in, whether the
5053+
// loaded module was built resiliently or not.
5054+
auto accessScope = getFormalAccessScope(/*useDC=*/nullptr,
5055+
/*treatUsableFromInlineAsPublic=*/true);
5056+
return !getDeclContext()->bypassResilienceInPackage(accessScope.isPackage());
50595057
}
50605058

50615059
DestructorDecl *NominalTypeDecl::getValueTypeDestructor() {
@@ -6375,7 +6373,11 @@ bool EnumDecl::isFormallyExhaustive(const DeclContext *useDC) const {
63756373
// Non-public, non-versioned enums are always exhaustive.
63766374
AccessScope accessScope = getFormalAccessScope(/*useDC*/nullptr,
63776375
/*respectVersioned*/true);
6378-
if (!accessScope.isPublic())
6376+
// Both public and package enums should behave the same unless
6377+
// package enum is optimized with bypassing resilience checks.
6378+
if (!accessScope.isPublicOrPackage())
6379+
return true;
6380+
if (useDC && useDC->bypassResilienceInPackage(accessScope.isPackage()))
63796381
return true;
63806382

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

lib/AST/DeclContext.cpp

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

303+
bool DeclContext::bypassResilienceInPackage(bool isForPackageDecl) const {
304+
// Bypassing resilience checks only applies to package types (and possibly
305+
// public types in the same package in the future). Allowed only if opted-in
306+
// for bypassing optimization, client and defining module are in the same
307+
// package, and defining module is a binary module.
308+
return isForPackageDecl &&
309+
getASTContext().LangOpts.EnableBypassResilienceInPackage &&
310+
getParentModule()->inSamePackage(getASTContext().MainModule) &&
311+
!getParentModule()->isBuiltFromInterface();
312+
}
313+
303314
ModuleDecl *DeclContext::getParentModule() const {
304315
// If the current context is PackageUnit, return the module
305316
// decl context pointing to the current context. This check

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))

lib/SILGen/SILGen.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1889,7 +1889,10 @@ static bool canStorageUseTrivialDescriptor(SILGenModule &SGM,
18891889
if (SGM.canStorageUseStoredKeyPathComponent(decl, expansion)) {
18901890
// External modules can't directly access storage, unless this is a
18911891
// property in a fixed-layout type.
1892-
return !decl->isFormallyResilient();
1892+
// Assert here as key path component cannot refer to a static var.
1893+
assert(!decl->isStatic());
1894+
// By this point, decl is a fixed layout or its enclosing type is non-resilient.
1895+
return true;
18931896
}
18941897

18951898
// If the type is computed and doesn't have a setter that's hidden from

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->inSamePackage(ctx.MainModule) &&
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: 6 additions & 8 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
@@ -223,7 +221,7 @@ public func memoryLayoutDotSizeWithResilientStruct() -> Int {
223221
// CHECK-LABEL: define{{.*}} swiftcc {{i32|i64}} @"$s18package_resilience40memoryLayoutDotStrideWithResilientStructSiyF"()
224222
public func memoryLayoutDotStrideWithResilientStruct() -> Int {
225223
// CHECK: ret [[INT]] [[#WORDSIZE + WORDSIZE]]
226-
return MemoryLayout<Size>.size
224+
return MemoryLayout<Size>.stride
227225
}
228226

229227
// CHECK-LABEL: define{{.*}} swiftcc {{i32|i64}} @"$s18package_resilience43memoryLayoutDotAlignmentWithResilientStructSiyF"()

0 commit comments

Comments
 (0)