Skip to content

Commit f066219

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 1adcf99 commit f066219

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
@@ -6394,7 +6394,12 @@ ArgumentSource AccessorBaseArgPreparer::prepareAccessorAddressBaseArg() {
63946394
// If the base is currently an address, we may have to copy it.
63956395
if (shouldLoadBaseAddress()) {
63966396
if (selfParam.isConsumed() ||
6397-
base.getType().isAddressOnly(SGF.F)) {
6397+
(base.getType().isAddressOnly(SGF.F)
6398+
// If a move-only base is borrowed, then we have to try our best to
6399+
// borrow it in-place without copying.
6400+
// TODO: Can we avoid copying a non-move-only value too in this
6401+
// circumstance?
6402+
&& !base.getType().isMoveOnly())) {
63986403
// The load can only be a take if the base is a +1 rvalue.
63996404
auto shouldTake = IsTake_t(base.hasCleanup());
64006405

@@ -6403,6 +6408,11 @@ ArgumentSource AccessorBaseArgPreparer::prepareAccessorAddressBaseArg() {
64036408
SGFContext(), shouldTake);
64046409
return ArgumentSource(loc, RValue(SGF, loc, baseFormalType, base));
64056410
}
6411+
6412+
// If the type is address-only, we can borrow the memory location as is.
6413+
if (base.getType().isAddressOnly(SGF.F)) {
6414+
return ArgumentSource(loc, RValue(SGF, loc, baseFormalType, base));
6415+
}
64066416

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

lib/SILGen/SILGenLValue.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,6 +1165,11 @@ namespace {
11651165
assert(base
11661166
&& base.getType().isAddress()
11671167
&& "should have an address base to borrow from");
1168+
// If the base value is address-only then we can borrow from the
1169+
// address in-place.
1170+
if (!base.getType().isLoadable(SGF.F)) {
1171+
return base;
1172+
}
11681173
auto result = SGF.B.createLoadBorrow(loc, base.getValue());
11691174
return SGF.emitFormalEvaluationManagedBorrowedRValueWithCleanup(loc,
11701175
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)