Skip to content

Commit 185022c

Browse files
committed
SILGen: Only skip decls nested in functions when the function is skipped.
If a function body is emitted, all of the declarations inside that function body must be emitted, too. Previously, lazy var initializers were being skipped regardless of whether the function containing them was skipped, resulting in SIL verification errors (which were correctly predicting linker errors). Resolves rdar://134708502.
1 parent d53f4fc commit 185022c

File tree

7 files changed

+51
-14
lines changed

7 files changed

+51
-14
lines changed

include/swift/AST/DeclContext.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,16 @@ class alignas(1 << DeclContextAlignInBits) DeclContext
496496
return const_cast<DeclContext *>(this)->getTopmostDeclarationDeclContext();
497497
}
498498

499+
/// This routine looks through closure, initializer, and local function
500+
/// contexts to find the outermost function declaration.
501+
///
502+
/// \returns the outermost function, or null if there is no such context.
503+
LLVM_READONLY
504+
DeclContext *getOutermostFunctionContext();
505+
const DeclContext *getOutermostFunctionContext() const {
506+
return const_cast<DeclContext *>(this)->getOutermostFunctionContext();
507+
}
508+
499509
/// Returns the innermost context that is an AbstractFunctionDecl whose
500510
/// body has been skipped.
501511
LLVM_READONLY

include/swift/AST/DeclExportabilityVisitor.h

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ class DeclExportabilityVisitor
3333
DeclExportabilityVisitor(){};
3434

3535
bool visit(const Decl *D) {
36+
// Declarations nested in fragile functions are exported.
37+
if (D->getDeclContext()->getFragileFunctionKind().kind !=
38+
FragileFunctionKind::None)
39+
return true;
40+
3641
if (auto value = dyn_cast<ValueDecl>(D)) {
3742
// A decl is exportable if it has a public access level.
3843
auto accessScope =
@@ -50,20 +55,6 @@ class DeclExportabilityVisitor
5055
bool visitDecl(const Decl *D) = delete;
5156
bool visitValueDecl(const ValueDecl *valueDecl) = delete;
5257

53-
bool visitAbstractFunctionDecl(const AbstractFunctionDecl *afd) {
54-
// If this function is nested within another function that is exportable to
55-
// clients then it is also exportable.
56-
auto dc = afd->getDeclContext();
57-
do {
58-
if (auto parent = dyn_cast<AbstractFunctionDecl>(dc)) {
59-
if (DeclExportabilityVisitor().visit(parent))
60-
return true;
61-
}
62-
} while ((dc = dc->getParent()));
63-
64-
return false;
65-
}
66-
6758
bool visitExtensionDecl(const ExtensionDecl *ext) {
6859
// Extensions must extend exportable types to be exportable.
6960
auto nominalType = ext->getExtendedNominal();
@@ -149,6 +140,7 @@ class DeclExportabilityVisitor
149140
DEFAULT_TO_ACCESS_LEVEL(TypeAlias);
150141
DEFAULT_TO_ACCESS_LEVEL(AssociatedType);
151142
DEFAULT_TO_ACCESS_LEVEL(AbstractStorage);
143+
DEFAULT_TO_ACCESS_LEVEL(AbstractFunction);
152144
DEFAULT_TO_ACCESS_LEVEL(Macro);
153145
DEFAULT_TO_ACCESS_LEVEL(EnumElement);
154146

lib/AST/DeclContext.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,22 @@ Decl *DeclContext::getTopmostDeclarationDeclContext() {
253253
return topmost;
254254
}
255255

256+
DeclContext *DeclContext::getOutermostFunctionContext() {
257+
AbstractFunctionDecl *result = nullptr;
258+
auto dc = this;
259+
do {
260+
if (auto afd = dyn_cast<AbstractFunctionDecl>(dc))
261+
result = afd;
262+
263+
// If we've found a non-local context, we don't have to keep walking up
264+
// the hierarchy.
265+
if (!dc->isLocalContext())
266+
break;
267+
} while ((dc = dc->getParent()));
268+
269+
return result;
270+
}
271+
256272
DeclContext *DeclContext::getInnermostSkippedFunctionContext() {
257273
auto dc = this;
258274
do {

lib/SILGen/SILGen.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,11 @@ bool SILGenModule::shouldSkipDecl(Decl *D) {
807807
if (!getASTContext().SILOpts.SkipNonExportableDecls)
808808
return false;
809809

810+
// Declarations nested in functions should be emitted whenever the function
811+
// containing them should also be emitted.
812+
if (auto funcContext = D->getDeclContext()->getOutermostFunctionContext())
813+
return shouldSkipDecl(funcContext->getAsDecl());
814+
810815
if (D->isExposedToClients())
811816
return false;
812817

test/Inputs/lazy_typecheck.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ public func publicFuncWithDefaultArg(_ x: Int = 1) -> Int {
2424
return NoTypecheck.int
2525
}
2626

27+
@inlinable public func publicInlinableFunc() -> Int {
28+
lazy var x = inlinableFunc()
29+
func nestedFunc() {}
30+
defer { nestedFunc() }
31+
return x
32+
}
33+
2734
package func packageFunc() -> Int {
2835
return NoTypecheck.int
2936
}

test/Inputs/lazy_typecheck_client.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ func testGlobalFunctions() {
1515
let _: Int = publicFunc()
1616
let _: Int = publicFuncReturnsTypealias()
1717
let _: Int = publicFuncWithDefaultArg()
18+
let _: Int = publicInlinableFunc()
1819
#if TEST_PACKAGE
1920
let _: Int = packageFunc()
2021
#endif

test/SILGen/skip_non_exportable_decls.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ internal func internalFunc() {}
2828
// CHECK-NO-SKIP: sil hidden{{.*}} @$s4Test022internalFuncWithNestedC0yyF : $@convention(thin) () -> () {
2929
// CHECK-SKIP-NOT: s4Test022internalFuncWithNestedC0yyF
3030
internal func internalFuncWithNestedFunc() {
31+
lazy var x = 1
3132
defer { internalFunc() }
3233
func nested() {}
3334
nested()
@@ -36,6 +37,9 @@ internal func internalFuncWithNestedFunc() {
3637
internalFunc()
3738
}()
3839
}
40+
// CHECK-NO-SKIP: sil private{{.*}} @$s4Test022internalFuncWithNestedC0yyF1xL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }) -> Int
41+
// CHECK-SKIP-NOT: s4Test022internalFuncWithNestedC0yyF1xL_Sivg
42+
3943
// CHECK-NO-SKIP: sil private{{.*}} @$s4Test022internalFuncWithNestedC0yyF6$deferL_yyF : $@convention(thin) () -> () {
4044
// CHECK-SKIP-NOT: s4Test022internalFuncWithNestedC0yyF6$deferL_yyF
4145

@@ -53,10 +57,12 @@ public func publicFunc() {}
5357

5458
// CHECK: sil{{.*}} @$s4Test25publicFuncWithNestedFuncsyyF : $@convention(thin) () -> () {
5559
public func publicFuncWithNestedFuncs() {
60+
lazy var x = 1
5661
defer { publicFunc() }
5762
func nested() {}
5863
nested()
5964
}
65+
// CHECK: sil private{{.*}} @$s4Test25publicFuncWithNestedFuncsyyF1xL_Sivg : $@convention(thin) (@guaranteed { var Optional<Int> }) -> Int
6066
// CHECK: sil private{{.*}} @$s4Test25publicFuncWithNestedFuncsyyF6$deferL_yyF : $@convention(thin) () -> () {
6167
// CHECK: sil private{{.*}} @$s4Test25publicFuncWithNestedFuncsyyF6nestedL_yyF : $@convention(thin) () -> () {
6268

0 commit comments

Comments
 (0)