Skip to content

Commit 10e0385

Browse files
committed
DI: Consider captures of let properties to be read-only uses.
Sema enforces that closures can't modify the let properties of their captured contexts, but we consider the capture argument to be @inout_aliasable at the SIL level. Add an exception to our normal handling so that this use is considered read-only. Fixes rdar://problem/40828667. A more principled solution would be to treat let captures as @in_guaranteed (see #17047), but that unfortunately leads to breakage elsewhere in the optimizer.
1 parent 7119842 commit 10e0385

File tree

2 files changed

+55
-1
lines changed

2 files changed

+55
-1
lines changed

lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -877,9 +877,32 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) {
877877
addElementUses(BaseEltNo, PointeeType, User, DIUseKind::IndirectIn);
878878
continue;
879879

880+
880881
// If this is an @inout parameter, it is like both a load and store.
881-
case ParameterConvention::Indirect_Inout:
882882
case ParameterConvention::Indirect_InoutAliasable: {
883+
// FIXME: The @inout_aliasable convention is used for indirect captures
884+
// of both 'let' and 'var' variables. Using a more specific convention
885+
// for 'let' properties like @in_guaranteed unfortunately exposes bugs
886+
// elsewhere in the pipeline. A 'let' capture cannot really be mutated
887+
// by the callee, and this is enforced by sema, so we can consider it
888+
// a nonmutating use.
889+
bool isLet = true;
890+
891+
for (unsigned i = 0; i < TheMemory.NumElements; ++i) {
892+
if (!TheMemory.isElementLetProperty(i)) {
893+
isLet = false;
894+
break;
895+
}
896+
}
897+
898+
if (isLet) {
899+
addElementUses(BaseEltNo, PointeeType, User, DIUseKind::IndirectIn);
900+
continue;
901+
}
902+
903+
LLVM_FALLTHROUGH;
904+
}
905+
case ParameterConvention::Indirect_Inout: {
883906
// If we're in the initializer for a struct, and this is a call to a
884907
// mutating method, we model that as an escape of self. If an
885908
// individual sub-member is passed as inout, then we model that as an
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %target-swift-frontend -emit-sil -verify %s
2+
3+
func foo<T>(a: Bool, t: T) {
4+
let x: T
5+
defer { print(x) }
6+
7+
x = t
8+
return
9+
}
10+
11+
func bar<T>(a: Bool, t: T) {
12+
let x: T // expected-note {{defined here}}
13+
defer { print(x) } //expected-error{{constant 'x' used before being initialized}}
14+
15+
if a {
16+
x = t
17+
return
18+
}
19+
}
20+
21+
func bas<T>(a: Bool, t: T) {
22+
let x: T
23+
defer { print(x) }
24+
25+
if a {
26+
x = t
27+
return
28+
}
29+
30+
x = t
31+
}

0 commit comments

Comments
 (0)