SILGen: Treat read-formal-accessed lvalues as borrows.

A read access asserts that the memory location is immutable for the duration
of the access, so it can be treated as a borrow rather than a mutable lvalue.
Doing this allows the borrow formal access scope fixes from #79084 to apply
to situations where a loadable type undergoes an accessor-based access with
indirect arguments (such as for public accessors when library evolution is
enabled for the type). Fixes rdar://143334632.
This commit is contained in:
Joe Groff
2025-02-03 19:18:40 -08:00
parent 85faa52009
commit 19329e3e03
8 changed files with 73 additions and 23 deletions

View File

@@ -8895,7 +8895,9 @@ class CopyValueInst
friend class SILBuilder;
CopyValueInst(SILDebugLocation DebugLoc, SILValue operand)
: UnaryInstructionBase(DebugLoc, operand, operand->getType()) {}
: UnaryInstructionBase(DebugLoc, operand, operand->getType()) {
assert(operand->getType().isObject());
}
};
class ExplicitCopyValueInst

View File

@@ -23,6 +23,16 @@
using namespace swift;
using namespace Lowering;
ManagedValue ManagedValue::forFormalAccessedAddress(SILValue address,
SGFAccessKind accessKind) {
if (isReadAccess(accessKind)) {
return forBorrowedAddressRValue(address);
} else {
return forLValue(address);
}
}
ManagedValue ManagedValue::forForwardedRValue(SILGenFunction &SGF,
SILValue value) {
if (!value)

View File

@@ -34,6 +34,7 @@ namespace Lowering {
class Initialization;
class SILGenFunction;
enum class SGFAccessKind : uint8_t;
/// ManagedValue - represents a singular SIL value and an optional cleanup.
/// Ownership of the ManagedValue can be "forwarded" to disable its cleanup when
@@ -252,6 +253,13 @@ public:
static ManagedValue forInContext() {
return ManagedValue(SILValue(), true, CleanupHandle::invalid());
}
/// Creates a managed value for an address that is undergoing a formal
/// access. This will be `forLValue` if the `accessKind` is a mutating
/// (exclusive) access or `forBorrowedRValueAddress` if the
/// `accessKind` is borrowing (shared).
static ManagedValue forFormalAccessedAddress(SILValue address,
SGFAccessKind accessKind);
bool isValid() const {
return valueAndFlag.getInt() || valueAndFlag.getPointer();

View File

@@ -618,7 +618,8 @@ namespace {
bool isFinal) override {
loc.markAutoGenerated();
assert(base.isLValue());
assert(base.isLValue() ||
(base.isPlusZero() && base.getType().isAddress()));
loc.markAutoGenerated();
SGF.B.createEndAccess(loc, base.getValue(), /*abort*/ false);
ExecutorHop.emit(SGF, loc);
@@ -655,7 +656,8 @@ static SILValue enterAccessScope(SILGenFunction &SGF, SILLocation loc,
/*fromBuiltin=*/false);
// Push a writeback to end it.
auto accessedMV = ManagedValue::forLValue(addr);
ManagedValue accessedMV
= ManagedValue::forFormalAccessedAddress(addr, accessKind);
std::unique_ptr<LogicalPathComponent> component(
new EndAccessPseudoComponent(typeData, std::move(prevExecutor)));
pushWriteback(SGF, loc, std::move(component), accessedMV,
@@ -673,7 +675,7 @@ static ManagedValue enterAccessScope(SILGenFunction &SGF, SILLocation loc,
bool noNestedConflict = false) {
auto access = enterAccessScope(SGF, loc, base, addr.getValue(), typeData,
accessKind, enforcement, actorIso, noNestedConflict);
return ManagedValue::forLValue(access);
return ManagedValue::forFormalAccessedAddress(access, accessKind);
}
// Find the base of the formal access at `address`. If the base requires an
@@ -1192,7 +1194,7 @@ namespace {
enterAccessScope(SGF, loc, base, addr, getTypeData(), getAccessKind(),
*Enforcement, takeActorIsolation());
return ManagedValue::forLValue(addr);
return ManagedValue::forFormalAccessedAddress(addr, getAccessKind());
}
bool isRValue() const override {
@@ -2292,7 +2294,20 @@ namespace {
static ManagedValue
makeBaseConsumableMaterializedRValue(SILGenFunction &SGF,
SILLocation loc, ManagedValue base) {
if (base.isLValue()) {
if (!SGF.useLoweredAddresses()
&& base.getType().isTrivial(SGF.F)
&& base.getType().isAddress()
&& !base.isLValue()) {
return SGF.emitLoad(loc, base.getValue(),
SGF.getTypeLowering(base.getType()), SGFContext(),
IsNotTake);
}
bool isBorrowed = base.isPlusZeroRValueOrTrivial()
&& !base.getType().isTrivial(SGF.F);
if (base.isLValue()
|| (isBorrowed && base.getType().isAddress())) {
if (SGF.useLoweredAddresses()) {
auto tmp = SGF.emitTemporaryAllocation(loc, base.getType());
SGF.B.createCopyAddr(loc, base.getValue(), tmp, IsNotTake,
@@ -2304,8 +2319,6 @@ makeBaseConsumableMaterializedRValue(SILGenFunction &SGF,
IsNotTake);
}
bool isBorrowed = base.isPlusZeroRValueOrTrivial()
&& !base.getType().isTrivial(SGF.F);
if (!base.getType().isAddress() || isBorrowed) {
if (SGF.useLoweredAddresses()) {
auto tmp = SGF.emitTemporaryAllocation(loc, base.getType());

View File

@@ -40,15 +40,15 @@ var lens = Lens(Rectangle(topLeft: topLeft,
bottomRight: bottomRight))
// CHECK: function_ref @$s29keypath_dynamic_member_lookup4LensV0B6MemberACyqd__Gs15WritableKeyPathCyxqd__G_tcluig
// CHECK-NEXT: apply %45<Rectangle, Point>({{.*}})
// CHECK-NEXT: apply %{{[0-9]+}}<Rectangle, Point>({{.*}})
// CHECK: function_ref @$s29keypath_dynamic_member_lookup4LensV0B6MemberACyqd__Gs7KeyPathCyxqd__G_tcluig
// CHECK-NEXT: apply %{{.*}}<Point, Int>({{.*}})
// CHECK-NEXT: apply %{{[0-9]+}}<Point, Int>({{.*}})
_ = lens.topLeft.x
// CHECK: function_ref @$s29keypath_dynamic_member_lookup4LensV0B6MemberACyqd__Gs15WritableKeyPathCyxqd__G_tcluig
// CHECK-NEXT: apply %68<Rectangle, Point>({{.*}})
// CHECK-NEXT: apply %{{[0-9]+}}<Rectangle, Point>({{.*}})
// CHECK: function_ref @$s29keypath_dynamic_member_lookup4LensV0B6MemberACyqd__Gs15WritableKeyPathCyxqd__G_tcluig
// CHECK-NEXT: apply {{%[0-9]+}}<Point, Int>({{.*}})
// CHECK-NEXT: apply %{{[0-9]+}}<Point, Int>({{.*}})
_ = lens.topLeft.y
lens.topLeft = Lens(Point(x: 1, y: 2)) // Ok

View File

@@ -0,0 +1,22 @@
// rdar://143334632
// RUN: %target-swift-emit-silgen -enable-library-evolution %s | %FileCheck %s
public struct Foo {
// CHECK-LABEL: sil {{.*}} @$s{{.*}}3FooV3foo
mutating func foo() -> Int {
// CHECK: [[BEGIN_READ:%.*]] = begin_access [read] [unknown] %0
// CHECK: ([[RESULT:%.*]], [[TOKEN:%.*]]) = begin_apply %{{.*}}([[BEGIN_READ]])
// CHECK: end_apply [[TOKEN]]
// CHECK: end_access [[BEGIN_READ]]
// CHECK: return [[RESULT]]
return self[]
}
var object : AnyObject
subscript() -> Int {
_read {fatalError()}
_modify {fatalError()}
}
}

View File

@@ -433,10 +433,8 @@ func test_handling_of_nonmutating_set() {
// CHECK: [[INIT_VALUE:%.*]] = function_ref @$s23assign_or_init_lowering32test_handling_of_nonmutating_setyyF4TestL_V5countSivpfi : $@convention(thin) () -> Int
// CHECK-NEXT: [[VALUE:%.*]] = apply [[INIT_VALUE]]() : $@convention(thin) () -> Int
// CHECK: assign_or_init [init] #<abstract function>Test.count, self [[SELF]] : $*Test, value [[VALUE]] : $Int, init {{.*}} : $@noescape @callee_guaranteed (Int) -> @out Int, set {{.*}} : $@noescape @callee_guaranteed (Int) -> ()
// CHECK: [[SELF_REF:%.*]] = begin_access [read] [static] [[SELF]] : $*Test
// CHECK: assign_or_init [set] #<abstract function>Test.count, self [[SELF_REF]] : $*Test, value %0 : $Int, init {{.*}} : $@noescape @callee_guaranteed (Int) -> @out Int, set {{.*}} : $@noescape @callee_guaranteed (Int) -> ()
// CHECK: [[SELF_REF:%.*]] = begin_access [read] [static] [[SELF]] : $*Test
// CHECK: assign_or_init [set] #<abstract function>Test.count, self [[SELF_REF]] : $*Test, value [[ZERO:%.*]] : $Int, init {{.*}} : $@noescape @callee_guaranteed (Int) -> @out Int, set {{.*}} : $@noescape @callee_guaranteed (Int) -> ()
// CHECK: assign_or_init [set] #<abstract function>Test.count, self [[SELF]] : $*Test, value %0 : $Int, init {{.*}} : $@noescape @callee_guaranteed (Int) -> @out Int, set {{.*}} : $@noescape @callee_guaranteed (Int) -> ()
// CHECK: assign_or_init [set] #<abstract function>Test.count, self [[SELF]] : $*Test, value [[ZERO:%.*]] : $Int, init {{.*}} : $@noescape @callee_guaranteed (Int) -> @out Int, set {{.*}} : $@noescape @callee_guaranteed (Int) -> ()
init(count: Int) {
self.count = count
self.count = 0
@@ -456,11 +454,10 @@ func test_handling_of_nonmutating_set() {
// CHECK-LABEL: sil private [ossa] @$s23assign_or_init_lowering32test_handling_of_nonmutating_setyyF14TestWithStoredL_V5countADSi_tcfC
// CHECK: [[SELF_REF:%.*]] = mark_uninitialized [rootself] %2
// CHECK: [[SELF_ACCESS:%.*]] = begin_access [read] [static] [[SELF_REF]] : $*TestWithStored
// CHECK: [[SETTER_REF:%.*]] = function_ref @$s23assign_or_init_lowering32test_handling_of_nonmutating_setyyF14TestWithStoredL_V5countSivs : $@convention(method) (Int, @guaranteed TestWithStored) -> ()
// CHECK: [[SELF:%.*]] = load [copy] {{.*}} : $*TestWithStored
// CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[SETTER_REF]]([[SELF]]) : $@convention(method) (Int, @guaranteed TestWithStored) -> ()
// CHECK-NEXT: assign_or_init [init] #<abstract function>TestWithStored.count, self [[SELF_ACCESS]] : $*TestWithStored, value %0 : $Int, init {{.*}} : $@noescape @callee_guaranteed (Int) -> @out Int, set [[SETTER_CLOSURE]] : $@noescape @callee_guaranteed (Int) -> ()
// CHECK-NEXT: assign_or_init [init] #<abstract function>TestWithStored.count, self [[SELF_REF]] : $*TestWithStored, value %0 : $Int, init {{.*}} : $@noescape @callee_guaranteed (Int) -> @out Int, set [[SETTER_CLOSURE]] : $@noescape @callee_guaranteed (Int) -> ()
init(count: Int) {
self.count = count
}
@@ -468,17 +465,15 @@ func test_handling_of_nonmutating_set() {
// CHECK-LABEL: sil private [ossa] @$s23assign_or_init_lowering32test_handling_of_nonmutating_setyyF14TestWithStoredL_V5valueADSi_tcfC
// CHECK: [[SELF:%.*]] = mark_uninitialized [rootself] %2
//
// CHECK: [[SELF_REF:%.*]] = begin_access [read] [static] [[SELF]] : $*TestWithStored
// CHECK: [[SETTER_REF:%.*]] = function_ref @$s23assign_or_init_lowering32test_handling_of_nonmutating_setyyF14TestWithStoredL_V5countSivs : $@convention(method) (Int, @guaranteed TestWithStored) -> ()
// CHECK: [[SELF_COPY:%.*]] = load [copy] {{.*}} : $*TestWithStored
// CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[SETTER_REF]]([[SELF_COPY]]) : $@convention(method) (Int, @guaranteed TestWithStored) -> ()
// CHECK-NEXT: assign_or_init [init] #<abstract function>TestWithStored.count, self [[SELF_REF]] : $*TestWithStored, value {{.*}} : $Int, init {{.*}} : $@noescape @callee_guaranteed (Int) -> @out Int, set [[SETTER_CLOSURE]] : $@noescape @callee_guaranteed (Int) -> ()
// CHECK-NEXT: assign_or_init [init] #<abstract function>TestWithStored.count, self [[SELF]] : $*TestWithStored, value {{.*}} : $Int, init {{.*}} : $@noescape @callee_guaranteed (Int) -> @out Int, set [[SETTER_CLOSURE]] : $@noescape @callee_guaranteed (Int) -> ()
//
// CHECK: [[SELF_REF:%.*]] = begin_access [read] [static] [[SELF]] : $*TestWithStored
// CHECK: [[SETTER_REF:%.*]] = function_ref @$s23assign_or_init_lowering32test_handling_of_nonmutating_setyyF14TestWithStoredL_V5countSivs : $@convention(method) (Int, @guaranteed TestWithStored) -> ()
// CHECK: [[SELF_COPY:%.*]] = load [copy] {{.*}} : $*TestWithStored
// CHECK-NEXT: [[SETTER_CLOSURE:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[SETTER_REF]]([[SELF_COPY]]) : $@convention(method) (Int, @guaranteed TestWithStored) -> ()
// CHECK-NEXT: assign_or_init [set] #<abstract function>TestWithStored.count, self [[SELF_REF]] : $*TestWithStored, value %0 : $Int, init {{.*}} : $@noescape @callee_guaranteed (Int) -> @out Int, set [[SETTER_CLOSURE]] : $@noescape @callee_guaranteed (Int) -> ()
// CHECK-NEXT: assign_or_init [set] #<abstract function>TestWithStored.count, self [[SELF]] : $*TestWithStored, value %0 : $Int, init {{.*}} : $@noescape @callee_guaranteed (Int) -> @out Int, set [[SETTER_CLOSURE]] : $@noescape @callee_guaranteed (Int) -> ()
init(value: Int) {
self.count = 0
self.count = value

View File

@@ -31,7 +31,7 @@ struct Test {
// CHECK-NEXT: [[INIT:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[INIT_REF]]([[METATYPE]]) : $@convention(thin) (@owned String, @thin Test.Type) -> @out String
// CHECK: [[SETTER_REF:%.*]] = function_ref @$s4main4TestV4nameSSvs : $@convention(method) (@owned String, @in_guaranteed Test) -> ()
// CHECK-NEXT: [[SETTER:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[SETTER_REF]]([[SELF_REF]]) : $@convention(method) (@owned String, @in_guaranteed Test) -> ()
// CHECK-NEXT: assign_or_init #Test.name, self [[SELF_REF]] : $*Test, value {{.*}} : $String, init [[INIT]] : $@noescape @callee_guaranteed (@owned String) -> @out String, set [[SETTER]] : $@noescape @callee_guaranteed (@owned String) -> ()
// CHECK-NEXT: assign_or_init #Test.name, self [[SELF]] : $*Test, value {{.*}} : $String, init [[INIT]] : $@noescape @callee_guaranteed (@owned String) -> @out String, set [[SETTER]] : $@noescape @callee_guaranteed (@owned String) -> ()
init(id: ID, name: String) {
self.id = id
self.name = name