Skip to content

Commit d0e519f

Browse files
committed
[SILGen]: ensure DI checks address-only let temporary allocations
Previously it was possible to reference uninitialized memory if a closure that was part of a variable binding initializer referenced the name of the uninitialized value. DI was ignoring these cases before because no mark_uninitialized instruction was produced. Instead of trying to skip emitting that instruction sometimes, just always emit it if a temporary allocation is used so DI will catch liveness issues.
1 parent aca07fb commit d0e519f

File tree

2 files changed

+15
-5
lines changed

2 files changed

+15
-5
lines changed

lib/SILGen/SILGenDecl.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,6 @@ class LetValueInitialization : public Initialization {
781781
// There are four cases we need to handle here: parameters, initialized (or
782782
// bound) decls, uninitialized ones, and async let declarations.
783783
bool needsTemporaryBuffer;
784-
bool isUninitialized = false;
785784

786785
assert(!isa<ParamDecl>(vd)
787786
&& "should not bind function params on this path");
@@ -790,14 +789,12 @@ class LetValueInitialization : public Initialization {
790789
// If this is a let-value without an initializer, then we need a temporary
791790
// buffer. DI will make sure it is only assigned to once.
792791
needsTemporaryBuffer = true;
793-
isUninitialized = true;
794792
} else if (vd->isAsyncLet()) {
795793
// If this is an async let, treat it like a let-value without an
796794
// initializer. The initializer runs concurrently in a child task,
797795
// and value will be initialized at the point the variable in the
798796
// async let is used.
799797
needsTemporaryBuffer = true;
800-
isUninitialized = true;
801798
} else {
802799
// If this is a let with an initializer or bound value, we only need a
803800
// buffer if the type is address only or is noncopyable.
@@ -824,8 +821,11 @@ class LetValueInitialization : public Initialization {
824821
address = SGF.emitTemporaryAllocation(vd, lowering->getLoweredType(),
825822
DoesNotHaveDynamicLifetime,
826823
isLexical, IsFromVarDecl);
827-
if (isUninitialized)
828-
address = SGF.B.createMarkUninitializedVar(vd, address);
824+
825+
// Ensure DI always checks this to avoid cases where an address-only
826+
// value is referenced in a closure that is part of its initializer.
827+
address = SGF.B.createMarkUninitializedVar(vd, address);
828+
829829
DestroyCleanup = SGF.enterDormantTemporaryCleanup(address, *lowering);
830830
SGF.VarLocs[vd] = SILGenFunction::VarLoc(address,
831831
SILAccessEnforcement::Unknown);

test/SILOptimizer/definite_init_address_only_let.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,13 @@ func quz<T>(a: Bool, t: T) {
3939
return
4040
}
4141
}
42+
43+
// https://github.com/swiftlang/swift/issues/84909
44+
45+
func uninit_closure_reference() {
46+
func passthrough(_ a: () -> Any) -> Any { a() }
47+
48+
let initMe = passthrough { initMe }
49+
// expected-error @-1 {{constant 'initMe' used before being initialized}}
50+
// expected-note @-2 {{defined here}}
51+
}

0 commit comments

Comments
 (0)