Skip to content

Commit 3c0601c

Browse files
committed
SILGen: Don't copy a borrowed noncopyable address-only base of a computed property access.
We can probably avoid this copy in more circumstances, but make the change only for noncopyable types for now, since that's the case where it's most semantically apparent. rdar://109161396
1 parent 67cf190 commit 3c0601c

File tree

4 files changed

+96
-5
lines changed

4 files changed

+96
-5
lines changed

lib/SILGen/SILGenApply.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6455,7 +6455,12 @@ ArgumentSource AccessorBaseArgPreparer::prepareAccessorAddressBaseArg() {
64556455
// If the base is currently an address, we may have to copy it.
64566456
if (shouldLoadBaseAddress()) {
64576457
if (selfParam.isConsumed() ||
6458-
base.getType().isAddressOnly(SGF.F)) {
6458+
(base.getType().isAddressOnly(SGF.F)
6459+
// If a move-only base is borrowed, then we have to try our best to
6460+
// borrow it in-place without copying.
6461+
// TODO: Can we avoid copying a non-move-only value too in this
6462+
// circumstance?
6463+
&& !base.getType().isMoveOnly())) {
64596464
// The load can only be a take if the base is a +1 rvalue.
64606465
auto shouldTake = IsTake_t(base.hasCleanup());
64616466

@@ -6464,6 +6469,11 @@ ArgumentSource AccessorBaseArgPreparer::prepareAccessorAddressBaseArg() {
64646469
SGFContext(), shouldTake);
64656470
return ArgumentSource(loc, RValue(SGF, loc, baseFormalType, base));
64666471
}
6472+
6473+
// If the type is address-only, we can borrow the memory location as is.
6474+
if (base.getType().isAddressOnly(SGF.F)) {
6475+
return ArgumentSource(loc, RValue(SGF, loc, baseFormalType, base));
6476+
}
64676477

64686478
// If we do not have a consumed base and need to perform a load, perform a
64696479
// formal access load borrow.

lib/SILGen/SILGenLValue.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,11 @@ namespace {
11711171
assert(base
11721172
&& base.getType().isAddress()
11731173
&& "should have an address base to borrow from");
1174+
// If the base value is address-only then we can borrow from the
1175+
// address in-place.
1176+
if (!base.getType().isLoadable(SGF.F)) {
1177+
return base;
1178+
}
11741179
auto result = SGF.B.createLoadBorrow(loc, base.getValue());
11751180
return SGF.emitFormalEvaluationManagedBorrowedRValueWithCleanup(loc,
11761181
base.getValue(), result);

test/ModuleInterface/moveonly_user.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,13 @@
55
// RUN: %target-swift-frontend -emit-sil -sil-verify-all -I %t %s > /dev/null
66

77
// >> now again with library evolution; we expect the same result.
8-
// FIXME: move checker doesn't like it when you specify library evolution
98
// RUN: %target-swift-frontend -DSYNTHESIZE_ACCESSORS -enable-library-evolution -emit-module -o %t/Hello.swiftmodule %S/Inputs/moveonly_api.swift
109
// RUN: %target-swift-frontend -emit-sil -sil-verify-all -I %t %s > /dev/null
1110

1211
// FIXME: ideally this would also try executing the program rather than just generating SIL
1312

1413
// FIXME: make this test work when we're not synthesizing the accessors
1514

16-
// rdar://106164128
17-
// XFAIL: *
18-
1915
import Hello
2016

2117
func simpleTest() {
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
2+
// RUN: %target-swift-frontend -emit-sil -verify %s
3+
4+
// rdar://109161396
5+
6+
public protocol P {}
7+
8+
@_moveOnly
9+
public struct M {
10+
private var x: P
11+
var other: CInt { 0 }
12+
13+
var otherMoveOnly: M {
14+
_read {
15+
yield self
16+
}
17+
}
18+
19+
@_silgen_name("no")
20+
init()
21+
}
22+
23+
// CHECK-LABEL: sil [ossa] @${{.*}}4test3mut
24+
// CHECK: [[CHECK:%.*]] = mark_must_check [consumable_and_assignable] %0
25+
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[CHECK]]
26+
// CHECK: [[RESULT:%.*]] = apply {{.*}}([[ACCESS]])
27+
// CHECK; end_access [[ACCESS]]
28+
// CHECK: return [[RESULT]]
29+
public func test(mut: inout M) -> CInt {
30+
return mut.other
31+
}
32+
33+
// CHECK-LABEL: sil [ossa] @${{.*}}4test6borrow
34+
// CHECK: [[CHECK:%.*]] = mark_must_check [no_consume_or_assign] %0
35+
// CHECK: [[RESULT:%.*]] = apply {{.*}}([[CHECK]])
36+
// CHECK: return [[RESULT]]
37+
public func test(borrow: borrowing M) -> CInt {
38+
return borrow.other
39+
}
40+
41+
// CHECK-LABEL: sil [ossa] @${{.*}}4test7consume
42+
// CHECK: [[BOX:%.*]] = project_box
43+
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[BOX]]
44+
// CHECK: [[CHECK:%.*]] = mark_must_check [no_consume_or_assign] [[ACCESS]]
45+
// CHECK: [[RESULT:%.*]] = apply {{.*}}([[CHECK]])
46+
// CHECK; end_access [[ACCESS]]
47+
// CHECK: return [[RESULT]]
48+
public func test(consume: consuming M) -> CInt {
49+
return consume.other
50+
}
51+
52+
// CHECK-LABEL: sil [ossa] @${{.*}}4test3own
53+
// CHECK: [[CHECK:%.*]] = mark_must_check [consumable_and_assignable] %0
54+
// CHECK: [[RESULT:%.*]] = apply {{.*}}([[CHECK]])
55+
// CHECK: return [[RESULT]]
56+
public func test(own: __owned M) -> CInt {
57+
return own.other
58+
}
59+
60+
func use(_: CInt, andMutate _: inout M) {}
61+
func use(_: CInt, andConsume _: consuming M) {}
62+
func borrow(_: borrowing M, andMutate _: inout M) {}
63+
func borrow(_: borrowing M, andConsume _: consuming M) {}
64+
65+
public func testNoInterferenceGet(mut: inout M, extra: consuming M) {
66+
// This should not cause exclusivity interference, since the result of
67+
// the getter can have an independent lifetime from the borrow.
68+
use(mut.other, andMutate: &mut)
69+
use(mut.other, andConsume: mut)
70+
mut = extra
71+
}
72+
73+
public func testInterferenceRead(mut: inout M, extra: consuming M) {
74+
// This should cause exclusivity interference, since in order to borrow
75+
// the yielded result from the `_read`, we need to keep the borrow of
76+
// the base going.
77+
borrow(mut.otherMoveOnly, andMutate: &mut) // expected-error{{}} expected-note{{}}
78+
borrow(mut.otherMoveOnly, andConsume: mut) // expected-error{{}} expected-note{{}}
79+
mut = extra
80+
}

0 commit comments

Comments
 (0)