Skip to content

Commit 497904d

Browse files
committed
Merge remote-tracking branch 'origin/master' into master-next
2 parents 4c4992d + 94be707 commit 497904d

File tree

6 files changed

+179
-36
lines changed

6 files changed

+179
-36
lines changed

lib/Sema/MiscDiagnostics.cpp

Lines changed: 78 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2276,7 +2276,8 @@ class VarDeclUsageChecker : public ASTWalker {
22762276
vdi->second |= Flag;
22772277
}
22782278

2279-
void markBaseOfAbstractStorageDeclStore(Expr *E, ConcreteDeclRef decl);
2279+
void markBaseOfStorageUse(Expr *E, ConcreteDeclRef decl, unsigned flags);
2280+
void markBaseOfStorageUse(Expr *E, bool isMutating);
22802281

22812282
void markStoredOrInOutExpr(Expr *E, unsigned Flags);
22822283

@@ -2655,29 +2656,57 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
26552656
}
26562657
}
26572658

2658-
/// Handle a store to "x.y" where 'base' is the expression for x and 'decl' is
2659-
/// the decl for 'y'.
2660-
void VarDeclUsageChecker::
2661-
markBaseOfAbstractStorageDeclStore(Expr *base, ConcreteDeclRef decl) {
2662-
// If the base is a class or an rvalue, then this store just loads the base.
2663-
if (base->getType()->isAnyClassReferenceType() ||
2664-
!(base->getType()->hasLValueType() || base->isSemanticallyInOutExpr())) {
2659+
/// Handle a use of "x.y" or "x[0]" where 'base' is the expression for x and
2660+
/// 'decl' is the property or subscript.
2661+
///
2662+
/// TODO: Rip this out and just rely on LValueAccessKind.
2663+
void VarDeclUsageChecker::markBaseOfStorageUse(Expr *base, ConcreteDeclRef decl,
2664+
unsigned flags) {
2665+
// If the base is an rvalue, then we know that this is a non-mutating access.
2666+
// Note that we can have mutating accesses even when the base has class or
2667+
// metatype type due to protocols and protocol extensions.
2668+
if (!base->getType()->hasLValueType() &&
2669+
!base->isSemanticallyInOutExpr()) {
26652670
base->walk(*this);
26662671
return;
26672672
}
26682673

2669-
// If the store is to a non-mutating member, then this is just a load, even
2670-
// if the base is an inout expr.
2671-
auto *ASD = cast<AbstractStorageDecl>(decl.getDecl());
2672-
if (ASD->isSettable(nullptr) && !ASD->isSetterMutating()) {
2673-
// Sema conservatively converts the base to inout expr when it is an lvalue.
2674-
// Look through it because we know it isn't actually doing a load/store.
2675-
if (auto *ioe = dyn_cast<InOutExpr>(base))
2676-
base = ioe->getSubExpr();
2674+
// Compute whether this access is to a mutating member.
2675+
auto *ASD = dyn_cast_or_null<AbstractStorageDecl>(decl.getDecl());
2676+
bool isMutating = false;
2677+
if (!ASD) {
2678+
// If there's no abstract storage declaration (which should hopefully
2679+
// only happen with invalid code), treat the base access as mutating if
2680+
// the subobject is being mutated and the base type is not a class
2681+
// or metatype.
2682+
if (flags & RK_Written) {
2683+
Type type = base->getType()->getRValueType()->getInOutObjectType();
2684+
if (!type->isAnyClassReferenceType() && !type->is<AnyMetatypeType>())
2685+
isMutating = true;
2686+
}
2687+
} else {
2688+
// Otherwise, consider whether the accessors are mutating.
2689+
if (flags & RK_Read)
2690+
isMutating |= ASD->isGetterMutating();
2691+
if (flags & RK_Written)
2692+
isMutating |= ASD->isSettable(nullptr) && ASD->isSetterMutating();
2693+
}
2694+
2695+
markBaseOfStorageUse(base, isMutating);
2696+
}
2697+
2698+
void VarDeclUsageChecker::markBaseOfStorageUse(Expr *base, bool isMutating) {
2699+
// CSApply sometimes wraps the base in an InOutExpr just because the
2700+
// base is an l-value; look through that so we can get more precise
2701+
// checking.
2702+
if (auto *ioe = dyn_cast<InOutExpr>(base))
2703+
base = ioe->getSubExpr();
2704+
2705+
if (!isMutating) {
26772706
base->walk(*this);
26782707
return;
26792708
}
2680-
2709+
26812710
// Otherwise this is a read and write of the base.
26822711
return markStoredOrInOutExpr(base, RK_Written|RK_Read);
26832712
}
@@ -2712,36 +2741,33 @@ void VarDeclUsageChecker::markStoredOrInOutExpr(Expr *E, unsigned Flags) {
27122741
if (auto *SE = dyn_cast<SubscriptExpr>(E)) {
27132742
// The index of the subscript is evaluated as an rvalue.
27142743
SE->getIndex()->walk(*this);
2715-
if (SE->hasDecl())
2716-
markBaseOfAbstractStorageDeclStore(SE->getBase(), SE->getDecl());
2717-
else // FIXME: Should not be needed!
2718-
markStoredOrInOutExpr(SE->getBase(), RK_Written|RK_Read);
2719-
2744+
markBaseOfStorageUse(SE->getBase(), SE->getDecl(), Flags);
27202745
return;
27212746
}
27222747

27232748
// Likewise for key path applications. An application of a WritableKeyPath
2724-
// reads and writes its base.
2749+
// reads and writes its base; an application of a ReferenceWritableKeyPath
2750+
// only reads its base; the other KeyPath types cannot be written at all.
27252751
if (auto *KPA = dyn_cast<KeyPathApplicationExpr>(E)) {
27262752
auto &C = KPA->getType()->getASTContext();
27272753
KPA->getKeyPath()->walk(*this);
2728-
if (KPA->getKeyPath()->getType()->getAnyNominal()
2729-
== C.getWritableKeyPathDecl())
2730-
markStoredOrInOutExpr(KPA->getBase(), RK_Written|RK_Read);
2731-
if (KPA->getKeyPath()->getType()->getAnyNominal()
2732-
== C.getReferenceWritableKeyPathDecl())
2733-
markStoredOrInOutExpr(KPA->getBase(), RK_Read);
2754+
2755+
bool isMutating =
2756+
(Flags & RK_Written) &&
2757+
KPA->getKeyPath()->getType()->getAnyNominal()
2758+
== C.getWritableKeyPathDecl();
2759+
markBaseOfStorageUse(KPA->getBase(), isMutating);
27342760
return;
27352761
}
27362762

27372763
if (auto *ioe = dyn_cast<InOutExpr>(E))
27382764
return markStoredOrInOutExpr(ioe->getSubExpr(), RK_Written|RK_Read);
27392765

27402766
if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
2741-
markBaseOfAbstractStorageDeclStore(MRE->getBase(), MRE->getMember());
2767+
markBaseOfStorageUse(MRE->getBase(), MRE->getMember(), Flags);
27422768
return;
27432769
}
2744-
2770+
27452771
if (auto *TEE = dyn_cast<TupleElementExpr>(E))
27462772
return markStoredOrInOutExpr(TEE->getBase(), Flags);
27472773

@@ -2750,6 +2776,12 @@ void VarDeclUsageChecker::markStoredOrInOutExpr(Expr *E, unsigned Flags) {
27502776

27512777
if (auto *BOE = dyn_cast<BindOptionalExpr>(E))
27522778
return markStoredOrInOutExpr(BOE->getSubExpr(), Flags);
2779+
2780+
// Bind existential expressions.
2781+
if (auto *OEE = dyn_cast<OpenExistentialExpr>(E)) {
2782+
OpaqueValueMap[OEE->getOpaqueValue()] = OEE->getExistentialValue();
2783+
return markStoredOrInOutExpr(OEE->getSubExpr(), Flags);
2784+
}
27532785

27542786
// If this is an OpaqueValueExpr that we've seen a mapping for, jump to the
27552787
// mapped value.
@@ -2788,8 +2820,16 @@ std::pair<bool, Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
27882820
if (auto VD = dyn_cast<VarDecl>(MRE->getMember().getDecl())) {
27892821
if (AssociatedGetter == VD && AssociatedGetterRefExpr == nullptr)
27902822
AssociatedGetterRefExpr = MRE;
2823+
markBaseOfStorageUse(MRE->getBase(), MRE->getMember(), RK_Read);
2824+
return { false, E };
27912825
}
27922826
}
2827+
if (auto SE = dyn_cast<SubscriptExpr>(E)) {
2828+
SE->getIndex()->walk(*this);
2829+
markBaseOfStorageUse(SE->getBase(), SE->getDecl(), RK_Read);
2830+
return { false, E };
2831+
}
2832+
27932833
// If this is an AssignExpr, see if we're mutating something that we know
27942834
// about.
27952835
if (auto *assign = dyn_cast<AssignExpr>(E)) {
@@ -2810,6 +2850,13 @@ std::pair<bool, Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
28102850
// If we see an OpenExistentialExpr, remember the mapping for its OpaqueValue.
28112851
if (auto *oee = dyn_cast<OpenExistentialExpr>(E))
28122852
OpaqueValueMap[oee->getOpaqueValue()] = oee->getExistentialValue();
2853+
2854+
// Visit bindings.
2855+
if (auto ove = dyn_cast<OpaqueValueExpr>(E)) {
2856+
if (auto mapping = OpaqueValueMap.lookup(ove))
2857+
mapping->walk(*this);
2858+
return { false, E };
2859+
}
28132860

28142861
// If we saw an ErrorExpr, take note of this.
28152862
if (isa<ErrorExpr>(E))

test/Constraints/anyhashable-collection-cast.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ func test() {
1818

1919
func testLValueCoerce() {
2020
var lvalue = "lvalue"
21-
var map: [AnyHashable : Any] = [lvalue: lvalue]
21+
let map: [AnyHashable : Any] = [lvalue: lvalue]
2222
lvalue = map[lvalue] as! String
2323
}

test/decl/var/usage.swift

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,22 +96,90 @@ func testInOut(_ x : inout Int) { // Ok.
9696

9797
struct TestStruct {
9898
var property = 42
99-
}
10099

100+
var mutatingProperty: Int {
101+
mutating get { return 0 }
102+
mutating set {}
103+
}
104+
var nonmutatingProperty: Int {
105+
nonmutating get { return 0 }
106+
nonmutating set {}
107+
}
108+
subscript(mutating index: Int) -> Int {
109+
mutating get { return 0 }
110+
mutating set {}
111+
}
112+
subscript(nonmutating index: Int) -> Int {
113+
nonmutating get { return 0 }
114+
nonmutating set {}
115+
}
116+
}
101117

102118
func testStructMember() -> TestStruct {
103119
var x = TestStruct() // ok
104120
x.property = 17
105121
return x
106122
}
107123

124+
func testMutatingProperty_get() -> TestStruct {
125+
var x = TestStruct() // ok
126+
_ = x.mutatingProperty
127+
return x
128+
}
129+
130+
func testMutatingProperty_set() -> TestStruct {
131+
var x = TestStruct() // ok
132+
x.mutatingProperty = 17
133+
return x
134+
}
135+
136+
func testNonmutatingProperty_get() -> TestStruct {
137+
var x = TestStruct() // expected-warning {{variable 'x' was never mutated; consider changing to 'let' constant}}
138+
_ = x.nonmutatingProperty
139+
return x
140+
}
141+
142+
func testNonmutatingProperty_set() -> TestStruct {
143+
var x = TestStruct() // expected-warning {{variable 'x' was never mutated; consider changing to 'let' constant}}
144+
x.nonmutatingProperty = 17
145+
return x
146+
}
147+
148+
func testMutatingSubscript_get() -> TestStruct {
149+
var x = TestStruct() // ok
150+
_ = x[mutating: 4]
151+
return x
152+
}
153+
154+
func testMutatingSubscript_set() -> TestStruct {
155+
var x = TestStruct() // ok
156+
x[mutating: 4] = 17
157+
return x
158+
}
159+
160+
func testNonmutatingSubscript_get() -> TestStruct {
161+
var x = TestStruct() // expected-warning {{variable 'x' was never mutated; consider changing to 'let' constant}}
162+
_ = x[nonmutating: 4]
163+
return x
164+
}
165+
166+
func testNonmutatingSubscript_set() -> TestStruct {
167+
var x = TestStruct() // expected-warning {{variable 'x' was never mutated; consider changing to 'let' constant}}
168+
x[nonmutating: 4] = 17
169+
return x
170+
}
108171

109172
func testSubscript() -> [Int] {
110173
var x = [1,2,3] // ok
111174
x[1] = 27
112175
return x
113176
}
114177

178+
func testSubscriptNeverMutated() -> [Int] {
179+
var x = [1,2,3] // expected-warning {{variable 'x' was never mutated; consider changing to 'let' constant}}
180+
_ = x[1]
181+
return x
182+
}
115183

116184
func testTuple(_ x : Int) -> Int {
117185
var x = x
@@ -178,7 +246,10 @@ func testBuildConfigs() {
178246
protocol Fooable {
179247
mutating func mutFoo()
180248
func immutFoo()
249+
var mutatingProperty: Int { mutating get mutating set }
250+
var nonmutatingProperty: Int { nonmutating get nonmutating set }
181251
}
252+
182253
func testOpenExistential(_ x: Fooable,
183254
y: Fooable) {
184255
var x = x
@@ -187,6 +258,25 @@ func testOpenExistential(_ x: Fooable,
187258
y.immutFoo()
188259
}
189260

261+
func testOpenExistential_mutatingProperty_get(p: Fooable) {
262+
var x = p
263+
_ = x.mutatingProperty
264+
}
265+
266+
func testOpenExistential_mutatingProperty_set(p: Fooable) {
267+
var x = p
268+
x.mutatingProperty = 4
269+
}
270+
271+
func testOpenExistential_nonmutatingProperty_get(p: Fooable) {
272+
var x = p // expected-warning {{variable 'x' was never mutated; consider changing to 'let' constant}}
273+
_ = x.nonmutatingProperty
274+
}
275+
276+
func testOpenExistential_nonmutatingProperty_set(p: Fooable) {
277+
var x = p // expected-warning {{variable 'x' was never mutated; consider changing to 'let' constant}}
278+
x.nonmutatingProperty = 4
279+
}
190280

191281
func couldThrow() throws {}
192282

test/multifile/lazy.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,9 @@ func test1(s: Test1) {
99
func test2(s: Test2) {
1010
_ = s.property
1111
}
12+
13+
// rdar://49482742 - shouldn't warn about 's' never being mutated
14+
func test3() {
15+
var s = Test1()
16+
_ = s.property
17+
}

test/stdlib/StringCompatibilityDiagnostics4.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %target-swift-frontend -typecheck -swift-version 4 %s -verify
22

33
func testPopFirst() {
4-
var str = "abc"
4+
let str = "abc"
55
var charView: String.CharacterView // expected-warning{{'CharacterView' is deprecated: Please use String directly}}
66
charView = str.characters // expected-warning{{'characters' is deprecated: Please use String directly}}
77
dump(charView)

test/type/infer/local_variables.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ func infer_type(_ i: Int, f: Float) {
2020

2121
func infer_generic_args() {
2222
// Simple types
23-
var x : Dictionary = ["Hello" : 1]
23+
let x : Dictionary = ["Hello" : 1]
2424
var i : Int = x["Hello"]!
2525

2626
// Tuples
27-
var (d, s) : (Dictionary, Array) = ( ["Hello" : 1], [1, 2, 3] )
27+
let (d, s) : (Dictionary, Array) = ( ["Hello" : 1], [1, 2, 3] )
2828
i = d["Hello"]!
2929
i = s[i]
3030

0 commit comments

Comments
 (0)