mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Add memory lifetime verification support for borrow accessors
This commit is contained in:
@@ -338,15 +338,18 @@ public:
|
||||
}
|
||||
|
||||
bool hasAddressResult() const {
|
||||
return hasGuaranteedAddressResult() || hasInoutResult();
|
||||
}
|
||||
|
||||
bool hasGuaranteedAddressResult() const {
|
||||
if (funcTy->getNumResults() != 1) {
|
||||
return false;
|
||||
}
|
||||
auto resultConvention = funcTy->getResults()[0].getConvention();
|
||||
if (silConv.loweredAddresses) {
|
||||
return resultConvention == ResultConvention::GuaranteedAddress ||
|
||||
resultConvention == ResultConvention::Inout;
|
||||
if (!silConv.loweredAddresses) {
|
||||
return false;
|
||||
}
|
||||
return resultConvention == ResultConvention::Inout;
|
||||
auto resultConvention = funcTy->getResults()[0].getConvention();
|
||||
return resultConvention == ResultConvention::GuaranteedAddress;
|
||||
}
|
||||
|
||||
struct SILResultTypeFunc;
|
||||
|
||||
@@ -3155,6 +3155,10 @@ public:
|
||||
return getSubstCalleeConv().hasGuaranteedResult();
|
||||
}
|
||||
|
||||
bool hasGuaranteedAddressResult() const {
|
||||
return getSubstCalleeConv().hasGuaranteedAddressResult();
|
||||
}
|
||||
|
||||
bool hasAddressResult() const {
|
||||
return getSubstCalleeConv().hasAddressResult();
|
||||
}
|
||||
|
||||
@@ -66,10 +66,15 @@ MemoryLocations::Location::Location(SILValue val, unsigned index, int parentIdx)
|
||||
representativeValue(val),
|
||||
parentIdx(parentIdx) {
|
||||
assert(((parentIdx >= 0) ==
|
||||
(isa<StructElementAddrInst>(val) || isa<TupleElementAddrInst>(val) ||
|
||||
isa<InitEnumDataAddrInst>(val) || isa<UncheckedTakeEnumDataAddrInst>(val) ||
|
||||
isa<InitExistentialAddrInst>(val) || isa<OpenExistentialAddrInst>(val)))
|
||||
&& "sub-locations can only be introduced with struct/tuple/enum projections");
|
||||
(isa<StructElementAddrInst>(val) || isa<TupleElementAddrInst>(val) ||
|
||||
isa<InitEnumDataAddrInst>(val) ||
|
||||
isa<UncheckedTakeEnumDataAddrInst>(val) ||
|
||||
isa<InitExistentialAddrInst>(val) ||
|
||||
isa<OpenExistentialAddrInst>(val) || isa<ApplyInst>(val) ||
|
||||
isa<StoreBorrowInst>(val))) &&
|
||||
"sub-locations can only be introduced with "
|
||||
"struct/tuple/enum/store_borrow/borrow accessor "
|
||||
"projections");
|
||||
setBitAndResize(subLocations, index);
|
||||
setBitAndResize(selfAndParents, index);
|
||||
}
|
||||
@@ -349,6 +354,21 @@ bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx
|
||||
if (cast<DebugValueInst>(user)->hasAddrVal())
|
||||
break;
|
||||
return false;
|
||||
case SILInstructionKind::ApplyInst: {
|
||||
auto *apply = cast<ApplyInst>(user);
|
||||
if (apply->hasAddressResult()) {
|
||||
if (!analyzeAddrProjection(apply, locIdx, 0, collectedVals,
|
||||
subLocationMap))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SILInstructionKind::StoreBorrowInst: {
|
||||
if (!analyzeAddrProjection(cast<StoreBorrowInst>(user), locIdx, 0,
|
||||
collectedVals, subLocationMap))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case SILInstructionKind::InjectEnumAddrInst:
|
||||
case SILInstructionKind::SelectEnumAddrInst:
|
||||
case SILInstructionKind::ExistentialMetatypeInst:
|
||||
@@ -357,13 +377,11 @@ bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx
|
||||
case SILInstructionKind::FixLifetimeInst:
|
||||
case SILInstructionKind::LoadInst:
|
||||
case SILInstructionKind::StoreInst:
|
||||
case SILInstructionKind::StoreBorrowInst:
|
||||
case SILInstructionKind::EndAccessInst:
|
||||
case SILInstructionKind::DestroyAddrInst:
|
||||
case SILInstructionKind::CheckedCastAddrBranchInst:
|
||||
case SILInstructionKind::UncheckedRefCastAddrInst:
|
||||
case SILInstructionKind::UnconditionalCheckedCastAddrInst:
|
||||
case SILInstructionKind::ApplyInst:
|
||||
case SILInstructionKind::TryApplyInst:
|
||||
case SILInstructionKind::BeginApplyInst:
|
||||
case SILInstructionKind::CopyAddrInst:
|
||||
@@ -371,6 +389,7 @@ bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx
|
||||
case SILInstructionKind::DeallocStackInst:
|
||||
case SILInstructionKind::SwitchEnumAddrInst:
|
||||
case SILInstructionKind::WitnessMethodInst:
|
||||
case SILInstructionKind::EndBorrowInst:
|
||||
break;
|
||||
case SILInstructionKind::MarkUnresolvedMoveAddrInst:
|
||||
// We do not want the memory lifetime verifier to verify move_addr inst
|
||||
|
||||
@@ -335,7 +335,7 @@ bool MemoryLifetimeVerifier::applyMayRead(Operand *argOp, SILValue addr) {
|
||||
|
||||
void MemoryLifetimeVerifier::requireNoStoreBorrowLocation(
|
||||
SILValue addr, SILInstruction *where) {
|
||||
if (isa<StoreBorrowInst>(addr)) {
|
||||
if (isStoreBorrowLocation(addr)) {
|
||||
reportError("store-borrow location cannot be written",
|
||||
locations.getLocationIdx(addr), where);
|
||||
}
|
||||
|
||||
@@ -8,26 +8,14 @@ public struct Wrapper {
|
||||
var k: Klass
|
||||
}
|
||||
|
||||
public struct GenWrapper<T> {
|
||||
@_hasStorage var _prop: T { get set }
|
||||
public var prop: T
|
||||
}
|
||||
|
||||
sil [ossa] @borrow_loadable_prop : $@convention(method) (@guaranteed Wrapper) -> @guaranteed Klass {
|
||||
bb0(%0 : @guaranteed $Wrapper):
|
||||
%2 = struct_extract %0, #Wrapper._k
|
||||
return %2
|
||||
}
|
||||
|
||||
sil [ossa] @borrow_addressonly_prop : $@convention(method) <T> (@in_guaranteed GenWrapper<T>) -> @guaranteed_address T {
|
||||
bb0(%0 : $*GenWrapper<T>):
|
||||
%2 = struct_element_addr %0, #GenWrapper._prop
|
||||
return %2
|
||||
}
|
||||
|
||||
sil @get_wrapper : $@convention(thin) () -> @owned Klass
|
||||
sil @use_klass : $@convention(thin) (@guaranteed Klass) -> ()
|
||||
sil @use_T : $@convention(thin) <T> (@in_guaranteed T) -> ()
|
||||
|
||||
// CHECK-LABEL: Error#: 0. Begin Error in Function: 'test_end_borrow_on_guaranteed_return_value'
|
||||
// CHECK: Invalid End Borrow!
|
||||
@@ -151,15 +139,3 @@ bb0(%0 : @owned $Wrapper):
|
||||
return %6
|
||||
}
|
||||
|
||||
// TODO: Add verification support in MemoryLifetimeVerifier
|
||||
sil [ossa] @test_use_after_free_address_only : $@convention(thin) <T> (@in GenWrapper<T>) -> () {
|
||||
bb0(%0 : $*GenWrapper<T>):
|
||||
%1 = function_ref @borrow_addressonly_prop : $@convention(method) <τ_0_0> (@in_guaranteed GenWrapper<τ_0_0>) -> @guaranteed_address τ_0_0
|
||||
%2 = apply %1<T>(%0) : $@convention(method) <τ_0_0> (@in_guaranteed GenWrapper<τ_0_0>) -> @guaranteed_address τ_0_0
|
||||
%3 = function_ref @use_T : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
||||
destroy_addr %0
|
||||
%5 = apply %3<T>(%2) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
||||
%6 = tuple ()
|
||||
return %6
|
||||
}
|
||||
|
||||
|
||||
@@ -532,6 +532,17 @@ bb0(%0 : @guaranteed $T):
|
||||
return %res : $()
|
||||
}
|
||||
|
||||
sil [ossa] @test_store_borrow_nested : $@convention(thin) (@guaranteed Inner) -> () {
|
||||
bb0(%0 : @guaranteed $Inner):
|
||||
%s = alloc_stack $Inner
|
||||
%sb = store_borrow %0 to %s
|
||||
%elem = struct_element_addr %sb, #Inner.a
|
||||
end_borrow %sb
|
||||
dealloc_stack %s
|
||||
%res = tuple ()
|
||||
return %res : $()
|
||||
}
|
||||
|
||||
sil [ossa] @test_cast_br_take_always : $@convention(thin) <U, V> (@in U) -> () {
|
||||
bb0(%0 : $*U):
|
||||
%s = alloc_stack $V
|
||||
|
||||
@@ -29,9 +29,24 @@ struct Mixed {
|
||||
var i: Int
|
||||
}
|
||||
|
||||
public struct InnerWrapper<T> {
|
||||
@_hasStorage var _prop: T { get set }
|
||||
public var prop: T
|
||||
}
|
||||
|
||||
public struct GenWrapper<T> {
|
||||
@_hasStorage var _prop: T { get set }
|
||||
public var prop: T
|
||||
|
||||
@_hasStorage var _nestedProp: InnerWrapper<T> { get set }
|
||||
public var nestedProp: InnerWrapper<T>
|
||||
}
|
||||
|
||||
sil @use_owned : $@convention(thin) (@owned T) -> ()
|
||||
sil @use_guaranteed : $@convention(thin) (@guaranteed T) -> ()
|
||||
sil @get_owned : $@convention(thin) () -> @owned T
|
||||
sil @use_T : $@convention(thin) <T> (@in_guaranteed T) -> ()
|
||||
sil @mutate_T : $@convention(thin) <T> (@inout T) -> ()
|
||||
|
||||
// CHECK: SIL memory lifetime failure in @test_simple: indirect argument is not alive at function return
|
||||
sil [ossa] @test_simple : $@convention(thin) (@inout T) -> @owned T {
|
||||
@@ -276,8 +291,8 @@ bb0(%0 : $*T):
|
||||
return %res : $()
|
||||
}
|
||||
|
||||
// CHECK: SIL memory lifetime failure in @test_store_borrow_destroy: store-borrow location cannot be written
|
||||
sil [ossa] @test_store_borrow_destroy : $@convention(thin) (@guaranteed T) -> () {
|
||||
// CHECK: SIL memory lifetime failure in @test_store_borrow_destroy1: store-borrow location cannot be written
|
||||
sil [ossa] @test_store_borrow_destroy1 : $@convention(thin) (@guaranteed T) -> () {
|
||||
bb0(%0 : @guaranteed $T):
|
||||
%s = alloc_stack $T
|
||||
%sb = store_borrow %0 to %s : $*T
|
||||
@@ -288,6 +303,19 @@ bb0(%0 : @guaranteed $T):
|
||||
return %res : $()
|
||||
}
|
||||
|
||||
// CHECK: SIL memory lifetime failure in @test_store_borrow_destroy2: store-borrow location cannot be written
|
||||
sil [ossa] @test_store_borrow_destroy2 : $@convention(thin) (@guaranteed Inner) -> () {
|
||||
bb0(%0 : @guaranteed $Inner):
|
||||
%s = alloc_stack $Inner
|
||||
%sb = store_borrow %0 to %s
|
||||
%elem = struct_element_addr %sb, #Inner.a
|
||||
destroy_addr %elem
|
||||
end_borrow %sb
|
||||
dealloc_stack %s
|
||||
%res = tuple ()
|
||||
return %res : $()
|
||||
}
|
||||
|
||||
sil [ossa] @func_with_inout_param : $@convention(thin) (@inout T) -> ()
|
||||
|
||||
// T-CHECK: SIL memory lifetime failure in @test_store_borrow_inout: store-borrow location cannot be written
|
||||
@@ -842,3 +870,81 @@ bb0(%0 : @owned $T, %1 : @owned $Inner):
|
||||
dealloc_stack %2
|
||||
return %8
|
||||
}
|
||||
|
||||
sil [ossa] @borrow_addressonly_prop : $@convention(method) <T> (@in_guaranteed GenWrapper<T>) -> @guaranteed_address T {
|
||||
bb0(%0 : $*GenWrapper<T>):
|
||||
%2 = struct_element_addr %0, #GenWrapper._prop
|
||||
return %2
|
||||
}
|
||||
|
||||
sil [ossa] @mutate_addressonly_prop : $@convention(method) <T> (@inout GenWrapper<T>) -> @inout T {
|
||||
bb0(%0 : $*GenWrapper<T>):
|
||||
%2 = struct_element_addr %0, #GenWrapper._prop
|
||||
return %2
|
||||
}
|
||||
|
||||
sil [ossa] @borrow_addressonly_nested_prop : $@convention(method) <T> (@in_guaranteed GenWrapper<T>) -> @guaranteed_address InnerWrapper<T> {
|
||||
bb0(%0 : $*GenWrapper<T>):
|
||||
%2 = struct_element_addr %0, #GenWrapper._nestedProp
|
||||
return %2
|
||||
}
|
||||
|
||||
sil [ossa] @mutate_addressonly_nested_prop : $@convention(method) <T> (@inout GenWrapper<T>) -> @inout InnerWrapper<T> {
|
||||
bb0(%0 : $*GenWrapper<T>):
|
||||
%2 = struct_element_addr %0, #GenWrapper._nestedProp
|
||||
return %2
|
||||
}
|
||||
|
||||
// CHECK: SIL memory lifetime failure in @test_use_after_free_address_only1: memory is not initialized, but should be
|
||||
// CHECK: memory location: %2 = apply %1<T>(%0) : $@convention(method) <τ_0_0> (@in_guaranteed GenWrapper<τ_0_0>) -> @guaranteed_address τ_0_0
|
||||
// CHECK: at instruction: %5 = apply %3<T>(%2) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
||||
sil [ossa] @test_use_after_free_address_only1 : $@convention(thin) <T> (@in GenWrapper<T>) -> () {
|
||||
bb0(%0 : $*GenWrapper<T>):
|
||||
%1 = function_ref @borrow_addressonly_prop : $@convention(method) <τ_0_0> (@in_guaranteed GenWrapper<τ_0_0>) -> @guaranteed_address τ_0_0
|
||||
%2 = apply %1<T>(%0) : $@convention(method) <τ_0_0> (@in_guaranteed GenWrapper<τ_0_0>) -> @guaranteed_address τ_0_0
|
||||
%3 = function_ref @use_T : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
||||
destroy_addr %0
|
||||
%5 = apply %3<T>(%2) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
||||
%6 = tuple ()
|
||||
return %6
|
||||
}
|
||||
|
||||
// CHECK: SIL memory lifetime failure in @test_use_after_free_address_only2: memory is not initialized, but should be
|
||||
// CHECK: memory location: %2 = apply %1<T>(%0) : $@convention(method) <τ_0_0> (@inout GenWrapper<τ_0_0>) -> @inout τ_0_0
|
||||
// CHECK: at instruction: %5 = apply %3<T>(%2) : $@convention(thin) <τ_0_0> (@inout τ_0_0) -> ()
|
||||
sil [ossa] @test_use_after_free_address_only2 : $@convention(thin) <T> (@in GenWrapper<T>) -> () {
|
||||
bb0(%0 : $*GenWrapper<T>):
|
||||
%1 = function_ref @mutate_addressonly_prop : $@convention(method) <τ_0_0> (@inout GenWrapper<τ_0_0>) -> @inout τ_0_0
|
||||
%2 = apply %1<T>(%0) : $@convention(method) <τ_0_0> (@inout GenWrapper<τ_0_0>) -> @inout τ_0_0
|
||||
%3 = function_ref @mutate_T : $@convention(thin) <τ_0_0> (@inout τ_0_0) -> ()
|
||||
destroy_addr %0
|
||||
%5 = apply %3<T>(%2) : $@convention(thin) <τ_0_0> (@inout τ_0_0) -> ()
|
||||
%6 = tuple ()
|
||||
return %6
|
||||
}
|
||||
|
||||
// TODO-CHECK: SIL memory lifetime failure in @test_guaranteed_address_consume: store-borrow location cannot be written
|
||||
// TODO-CHECK: memory location: %2 = apply %1<T>(%0) : $@convention(method) <τ_0_0> (@in_guaranteed GenWrapper<τ_0_0>) -> @guaranteed_address τ_0_0
|
||||
// TODO-CHECK: at instruction: destroy_addr %2 : $*T
|
||||
sil [ossa] @test_guaranteed_address_consume : $@convention(thin) <T> (@in GenWrapper<T>) -> () {
|
||||
bb0(%0 : $*GenWrapper<T>):
|
||||
%1 = function_ref @borrow_addressonly_prop : $@convention(method) <τ_0_0> (@in_guaranteed GenWrapper<τ_0_0>) -> @guaranteed_address τ_0_0
|
||||
%2 = apply %1<T>(%0) : $@convention(method) <τ_0_0> (@in_guaranteed GenWrapper<τ_0_0>) -> @guaranteed_address τ_0_0
|
||||
destroy_addr %2
|
||||
%4 = tuple ()
|
||||
return %4
|
||||
}
|
||||
|
||||
// TODO-CHECK: SIL memory lifetime failure in @test_guaranteed_nested_address_consume: store-borrow location cannot be written
|
||||
// TODO-CHECK: memory location: %3 = struct_element_addr %2 : $*InnerWrapper<T>, #InnerWrapper._prop
|
||||
// TODO-CHECK: at instruction: destroy_addr %3 : $*T
|
||||
sil [ossa] @test_guaranteed_nested_address_consume : $@convention(thin) <T> (@in GenWrapper<T>) -> () {
|
||||
bb0(%0 : $*GenWrapper<T>):
|
||||
%1 = function_ref @borrow_addressonly_nested_prop : $@convention(method) <τ_0_0> (@in_guaranteed GenWrapper<τ_0_0>) -> @guaranteed_address InnerWrapper<τ_0_0>
|
||||
%2 = apply %1<T>(%0) : $@convention(method) <τ_0_0> (@in_guaranteed GenWrapper<τ_0_0>) -> @guaranteed_address InnerWrapper<τ_0_0>
|
||||
%3 = struct_element_addr %2, #InnerWrapper._prop
|
||||
destroy_addr %3
|
||||
%4 = tuple ()
|
||||
return %4
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user