Skip to content

Commit e6273de

Browse files
authored
Merge pull request #17111 from jckarter/defer-let-inout-bug-4.2
[4.2] DI: Consider capture of `let`s to be a read-only use.
2 parents 93a22b6 + 29bae01 commit e6273de

File tree

7 files changed

+78
-18
lines changed

7 files changed

+78
-18
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

lib/Sema/ConstraintSystem.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,7 @@ std::pair<Type, Type>
873873
ConstraintSystem::getTypeOfReference(ValueDecl *value,
874874
FunctionRefKind functionRefKind,
875875
ConstraintLocatorBuilder locator,
876+
DeclContext *useDC,
876877
const DeclRefExpr *base) {
877878
if (value->getDeclContext()->isTypeContext() && isa<FuncDecl>(value)) {
878879
// Unqualified lookup can find operator names within nominal types.
@@ -937,7 +938,7 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value,
937938
// Unqualified reference to a type.
938939
if (auto typeDecl = dyn_cast<TypeDecl>(value)) {
939940
// Resolve the reference to this type declaration in our current context.
940-
auto type = TC.resolveTypeInContext(typeDecl, nullptr, DC,
941+
auto type = TC.resolveTypeInContext(typeDecl, nullptr, useDC,
941942
TypeResolutionFlags::InExpression,
942943
/*isSpecialized=*/false);
943944

@@ -958,7 +959,7 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value,
958959

959960
// Determine the type of the value, opening up that type if necessary.
960961
bool wantInterfaceType = !varDecl->getDeclContext()->isLocalContext();
961-
Type valueType = TC.getUnopenedTypeOfReference(varDecl, Type(), DC, base,
962+
Type valueType = TC.getUnopenedTypeOfReference(varDecl, Type(), useDC, base,
962963
wantInterfaceType);
963964

964965
assert(!valueType->hasUnboundGenericType() &&
@@ -1222,7 +1223,7 @@ ConstraintSystem::getTypeOfMemberReference(
12221223

12231224
// If the base is a module type, just use the type of the decl.
12241225
if (baseObjTy->is<ModuleType>()) {
1225-
return getTypeOfReference(value, functionRefKind, locator, base);
1226+
return getTypeOfReference(value, functionRefKind, locator, useDC, base);
12261227
}
12271228

12281229
// Don't open existentials when accessing typealias members of
@@ -1633,7 +1634,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
16331634
} else {
16341635
std::tie(openedFullType, refType)
16351636
= getTypeOfReference(choice.getDecl(),
1636-
choice.getFunctionRefKind(), locator);
1637+
choice.getFunctionRefKind(), locator, useDC);
16371638
}
16381639

16391640
// For a non-subscript declaration found via dynamic lookup, strip

lib/Sema/ConstraintSystem.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2188,6 +2188,7 @@ class ConstraintSystem {
21882188
ValueDecl *decl,
21892189
FunctionRefKind functionRefKind,
21902190
ConstraintLocatorBuilder locator,
2191+
DeclContext *useDC,
21912192
const DeclRefExpr *base = nullptr);
21922193

21932194
/// \brief Retrieve the type of a reference to the given value declaration,

test/SILGen/closures.swift

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -380,13 +380,11 @@ func closeOverLetLValue() {
380380
// CHECK: [[TMP_CLASS_ADDR:%.*]] = alloc_stack $ClassWithIntProperty, let, name "a", argno 1
381381
// CHECK: [[COPY_ARG:%.*]] = copy_value [[ARG]]
382382
// CHECK: store [[COPY_ARG]] to [init] [[TMP_CLASS_ADDR]] : $*ClassWithIntProperty
383-
// CHECK: [[LOADED_CLASS:%.*]] = load [copy] [[TMP_CLASS_ADDR]] : $*ClassWithIntProperty
384-
// CHECK: [[BORROWED_LOADED_CLASS:%.*]] = begin_borrow [[LOADED_CLASS]]
383+
// CHECK: [[BORROWED_LOADED_CLASS:%.*]] = load_borrow [[TMP_CLASS_ADDR]] : $*ClassWithIntProperty
385384
// CHECK: [[INT_IN_CLASS_ADDR:%.*]] = ref_element_addr [[BORROWED_LOADED_CLASS]] : $ClassWithIntProperty, #ClassWithIntProperty.x
386385
// CHECK: [[ACCESS:%.*]] = begin_access [read] [dynamic] [[INT_IN_CLASS_ADDR]] : $*Int
387386
// CHECK: [[INT_IN_CLASS:%.*]] = load [trivial] [[ACCESS]] : $*Int
388-
// CHECK: end_borrow [[BORROWED_LOADED_CLASS]] from [[LOADED_CLASS]]
389-
// CHECK: destroy_value [[LOADED_CLASS]]
387+
// CHECK: end_borrow [[BORROWED_LOADED_CLASS]] from [[TMP_CLASS_ADDR]]
390388
// CHECK: destroy_addr [[TMP_CLASS_ADDR]] : $*ClassWithIntProperty
391389
// CHECK: dealloc_stack %1 : $*ClassWithIntProperty
392390
// CHECK: return [[INT_IN_CLASS]]
@@ -397,10 +395,8 @@ func closeOverLetLValue() {
397395
// GUARANTEED: [[TMP:%.*]] = alloc_stack $ClassWithIntProperty
398396
// GUARANTEED: [[COPY:%.*]] = copy_value %0 : $ClassWithIntProperty
399397
// GUARANTEED: store [[COPY]] to [init] [[TMP]] : $*ClassWithIntProperty
400-
// GUARANTEED: [[LOADED_COPY:%.*]] = load [copy] [[TMP]]
401-
// GUARANTEED: [[BORROWED:%.*]] = begin_borrow [[LOADED_COPY]]
402-
// GUARANTEED: end_borrow [[BORROWED]] from [[LOADED_COPY]]
403-
// GUARANTEED: destroy_value [[LOADED_COPY]]
398+
// GUARANTEED: [[BORROWED:%.*]] = load_borrow [[TMP]]
399+
// GUARANTEED: end_borrow [[BORROWED]] from [[TMP]]
404400
// GUARANTEED: destroy_addr [[TMP]]
405401
// GUARANTEED: } // end sil function '$S8closures18closeOverLetLValueyyFSiyXEfU_'
406402

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+
}

test/SILOptimizer/definite_init_diagnostics.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,6 @@ func test2() {
8383
}
8484
b4 = 7
8585

86-
let b5: Any
87-
b5 = "x"
88-
{ takes_inout_any(&b5) }() // expected-error {{immutable value 'b5' must not be passed inout}}
89-
({ takes_inout_any(&b5) })() // expected-error {{immutable value 'b5' must not be passed inout}}
90-
9186
// Structs
9287
var s1 : SomeStruct
9388
s1 = SomeStruct() // ok

test/expr/closure/let.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %target-swift-frontend -typecheck -verify %s
2+
3+
func frob(x: inout Int) {}
4+
5+
func foo() {
6+
let x: Int // expected-note* {{}}
7+
8+
x = 0
9+
10+
_ = { frob(x: x) }() // expected-error{{'x' is a 'let'}}
11+
_ = { x = 0 }() // expected-error{{'x' is a 'let'}}
12+
_ = { frob(x: x); x = 0 }() // expected-error 2 {{'x' is a 'let'}}
13+
}

0 commit comments

Comments
 (0)