Skip to content

Commit 7062f21

Browse files
committed
SILGen: Make sure to emit closure bodies only once
We would potentially emit a closure multiple times when converting a closure to a @convention(c) type. This would result in a compiler crash if a stored property of @convention(c) type had an initializer expression and the containing type declaration had multiple initializers. Fixes <rdar://problem/25632886>.
1 parent 5a17ec4 commit 7062f21

File tree

4 files changed

+30
-14
lines changed

4 files changed

+30
-14
lines changed

lib/SILGen/SILGen.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,9 +696,18 @@ void SILGenModule::emitEnumConstructor(EnumElementDecl *decl) {
696696
}
697697

698698
SILFunction *SILGenModule::emitClosure(AbstractClosureExpr *ce) {
699-
// Closures are emitted by need, so don't required delayed emission.
700699
SILDeclRef constant(ce);
701700
SILFunction *f = getFunction(constant, ForDefinition);
701+
702+
// Generate the closure function, if we haven't already.
703+
//
704+
// We may visit the same closure expr multiple times in some cases.
705+
// For instance, when closures appear as in-line initializers of stored
706+
// properties, in which case the closure will be emitted into every
707+
// initializer of the containing type.
708+
if (!f->isExternalDeclaration())
709+
return f;
710+
702711
preEmitFunction(constant, ce, f, ce);
703712
PrettyStackTraceSILFunction X("silgen closureexpr", f);
704713
SILGenFunction(*this, *f).emitClosure(ce);

lib/SILGen/SILGenApply.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,13 +1177,13 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
11771177
}
11781178

11791179
void visitAbstractClosureExpr(AbstractClosureExpr *e) {
1180+
// Emit the closure body.
1181+
SGF.SGM.emitClosure(e);
1182+
11801183
// A directly-called closure can be emitted as a direct call instead of
11811184
// really producing a closure object.
11821185
SILDeclRef constant(e);
1183-
// Emit the closure function if we haven't yet.
1184-
if (!SGF.SGM.hasFunction(constant))
1185-
SGF.SGM.emitClosure(e);
1186-
1186+
11871187
ArrayRef<Substitution> subs;
11881188
CanFunctionType substFnType = getSubstFnType();
11891189

lib/SILGen/SILGenExpr.cpp

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,10 +1293,12 @@ RValue RValueEmitter::visitFunctionConversionExpr(FunctionConversionExpr *e,
12931293
CanAnyFunctionType destRepTy =
12941294
cast<FunctionType>(e->getType()->getCanonicalType());
12951295

1296-
if (destRepTy->getRepresentation() == FunctionTypeRepresentation::CFunctionPointer) {
1296+
if (destRepTy->getRepresentation() ==
1297+
FunctionTypeRepresentation::CFunctionPointer) {
12971298
ManagedValue result;
12981299

1299-
if (srcRepTy->getRepresentation() != FunctionTypeRepresentation::CFunctionPointer) {
1300+
if (srcRepTy->getRepresentation() !=
1301+
FunctionTypeRepresentation::CFunctionPointer) {
13001302
// A "conversion" of a DeclRef a C function pointer is done by referencing
13011303
// the thunk (or original C function) with the C calling convention.
13021304
result = emitCFunctionPointer(SGF, e);
@@ -1961,13 +1963,8 @@ RValue RValueEmitter::visitCaptureListExpr(CaptureListExpr *E, SGFContext C) {
19611963

19621964
RValue RValueEmitter::visitAbstractClosureExpr(AbstractClosureExpr *e,
19631965
SGFContext C) {
1964-
// Generate the closure function, if we haven't already.
1965-
// We may visit the same closure expr multiple times in some cases, for
1966-
// instance, when closures appear as in-line initializers of stored properties,
1967-
// in which case the closure will be emitted into every initializer of the
1968-
// containing type.
1969-
if (!SGF.SGM.hasFunction(SILDeclRef(e)))
1970-
SGF.SGM.emitClosure(e);
1966+
// Emit the closure body.
1967+
SGF.SGM.emitClosure(e);
19711968

19721969
// Generate the closure value (if any) for the closure expr's function
19731970
// reference.

test/SILGen/c_function_pointers.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,13 @@ func unsupported(_ a: Any) -> Int { return 0 }
5151
func pointers_to_bad_swift_functions(_ x: Int) {
5252
calls(unsupported, x) // expected-error{{C function pointer signature '(Any) -> Int' is not compatible with expected type '@convention(c) Int -> Int'}}
5353
}
54+
55+
// CHECK-LABEL: sil shared @_TFIvV19c_function_pointers22StructWithInitializers3fn1cT_T_iU_FT_T_ : $@convention(thin) () -> () {
56+
// CHECK-LABEL: sil shared [thunk] @_TToFIvV19c_function_pointers22StructWithInitializers3fn1cT_T_iU_FT_T_ : $@convention(c) () -> () {
57+
58+
struct StructWithInitializers {
59+
let fn1: @convention(c) () -> () = {}
60+
61+
init(a: ()) {}
62+
init(b: ()) {}
63+
}

0 commit comments

Comments
 (0)