Skip to content

Commit 3b227e9

Browse files
authored
Merge pull request #17066 from jckarter/defer-let-inout-bug-2
DI: Consider capture of `let`s to be a read-only use.
2 parents ae5715e + 10e0385 commit 3b227e9

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
@@ -862,9 +862,32 @@ void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) {
862862
addElementUses(BaseEltNo, PointeeType, User, DIUseKind::IndirectIn);
863863
continue;
864864

865+
865866
// If this is an @inout parameter, it is like both a load and store.
866-
case ParameterConvention::Indirect_Inout:
867867
case ParameterConvention::Indirect_InoutAliasable: {
868+
// FIXME: The @inout_aliasable convention is used for indirect captures
869+
// of both 'let' and 'var' variables. Using a more specific convention
870+
// for 'let' properties like @in_guaranteed unfortunately exposes bugs
871+
// elsewhere in the pipeline. A 'let' capture cannot really be mutated
872+
// by the callee, and this is enforced by sema, so we can consider it
873+
// a nonmutating use.
874+
bool isLet = true;
875+
876+
for (unsigned i = 0; i < TheMemory.NumElements; ++i) {
877+
if (!TheMemory.isElementLetProperty(i)) {
878+
isLet = false;
879+
break;
880+
}
881+
}
882+
883+
if (isLet) {
884+
addElementUses(BaseEltNo, PointeeType, User, DIUseKind::IndirectIn);
885+
continue;
886+
}
887+
888+
LLVM_FALLTHROUGH;
889+
}
890+
case ParameterConvention::Indirect_Inout: {
868891
// If we're in the initializer for a struct, and this is a call to a
869892
// mutating method, we model that as an escape of self. If an
870893
// 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
@@ -900,6 +900,7 @@ std::pair<Type, Type>
900900
ConstraintSystem::getTypeOfReference(ValueDecl *value,
901901
FunctionRefKind functionRefKind,
902902
ConstraintLocatorBuilder locator,
903+
DeclContext *useDC,
903904
const DeclRefExpr *base) {
904905
if (value->getDeclContext()->isTypeContext() && isa<FuncDecl>(value)) {
905906
// Unqualified lookup can find operator names within nominal types.
@@ -966,7 +967,7 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value,
966967
// Unqualified reference to a type.
967968
if (auto typeDecl = dyn_cast<TypeDecl>(value)) {
968969
// Resolve the reference to this type declaration in our current context.
969-
auto type = TC.resolveTypeInContext(typeDecl, nullptr, DC,
970+
auto type = TC.resolveTypeInContext(typeDecl, nullptr, useDC,
970971
TypeResolutionFlags::InExpression,
971972
/*isSpecialized=*/false);
972973

@@ -987,7 +988,7 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value,
987988

988989
// Determine the type of the value, opening up that type if necessary.
989990
bool wantInterfaceType = !varDecl->getDeclContext()->isLocalContext();
990-
Type valueType = TC.getUnopenedTypeOfReference(varDecl, Type(), DC, base,
991+
Type valueType = TC.getUnopenedTypeOfReference(varDecl, Type(), useDC, base,
991992
wantInterfaceType);
992993

993994
assert(!valueType->hasUnboundGenericType() &&
@@ -1258,7 +1259,7 @@ ConstraintSystem::getTypeOfMemberReference(
12581259

12591260
// If the base is a module type, just use the type of the decl.
12601261
if (baseObjTy->is<ModuleType>()) {
1261-
return getTypeOfReference(value, functionRefKind, locator, base);
1262+
return getTypeOfReference(value, functionRefKind, locator, useDC, base);
12621263
}
12631264

12641265
// Don't open existentials when accessing typealias members of
@@ -1671,7 +1672,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator,
16711672
} else {
16721673
std::tie(openedFullType, refType)
16731674
= getTypeOfReference(choice.getDecl(),
1674-
choice.getFunctionRefKind(), locator);
1675+
choice.getFunctionRefKind(), locator, useDC);
16751676
}
16761677

16771678
// 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
@@ -2203,6 +2203,7 @@ class ConstraintSystem {
22032203
ValueDecl *decl,
22042204
FunctionRefKind functionRefKind,
22052205
ConstraintLocatorBuilder locator,
2206+
DeclContext *useDC,
22062207
const DeclRefExpr *base = nullptr);
22072208

22082209
/// \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)