// RUN: %target-sil-opt -enable-sil-verify-all %s -enforce-exclusivity=unchecked -diagnose-static-exclusivity -verify | %FileCheck %s sil_stage raw import Builtin import Swift sil @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () sil @takesOneInout : $@convention(thin) (@inout Int) -> () sil @makesInt : $@convention(thin) () -> Int sil @takesInoutAndNoEscapeClosure : $@convention(thin) (@inout Int, @noescape @owned @callee_owned () -> ()) -> () sil @takesInoutAndNoEscapeClosureTakingArgument : $@convention(thin) (@inout Int, @noescape @owned @callee_owned (Int) -> ()) -> () sil @takesInoutAndNoEscapeGuaranteedClosureTakingArgument : $@convention(thin) (@inout Int, @noescape @guaranteed @callee_guaranteed (Int) -> ()) -> () sil @takesInoutAndNoEscapeClosureWithGenericReturn : $@convention(thin) (@inout Int, @noescape @callee_owned (Int) -> @out T) -> () sil @takesInoutAndNoEscapeBlockClosure : $@convention(thin) (@inout Int, @owned @convention(block) @noescape () -> ()) -> () sil @takesInoutAndNoEscapeOptionalBlockClosure : $@convention(thin) (@inout Int, @owned Optional<@convention(block) @noescape () -> ()>) -> () // CHECK-LABEL: sil hidden [ossa] @twoLocalInoutsDisaliased sil hidden [ossa] @twoLocalInoutsDisaliased : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %5 = alloc_box ${ var Int } %6 = project_box %5 : ${ var Int }, 0 store %0 to [trivial] %6 : $*Int %8 = function_ref @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () %9 = begin_access [modify] [unknown] %3 : $*Int %10 = begin_access [modify] [unknown] %6 : $*Int // no-error %11 = apply %8(%9, %10) : $@convention(thin) (@inout Int, @inout Int) -> () end_access %10 : $*Int end_access %9: $*Int destroy_value %5 : ${ var Int } destroy_value %2 : ${ var Int } %14 = tuple () return %14 : $() } // CHECK-LABEL: sil hidden [ossa] @twoLocalInoutsSimpleAliasing sil hidden [ossa] @twoLocalInoutsSimpleAliasing : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () %5 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %6 = begin_access [modify] [unknown] %3 : $*Int // expected-note {{conflicting access is here}} %7 = apply %4(%5, %6) : $@convention(thin) (@inout Int, @inout Int) -> () end_access %6 : $*Int end_access %5: $*Int destroy_value %2 : ${ var Int } %8 = tuple () return %8 : $() } // CHECK-LABEL: sil hidden [ossa] @conflictingPriorAccess sil hidden [ossa] @conflictingPriorAccess : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () %5 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %6 = begin_access [modify] [unknown] %5 : $*Int %7 = begin_access [modify] [unknown] %3 : $*Int // expected-note {{conflicting access is here}} %8 = apply %4(%5, %6) : $@convention(thin) (@inout Int, @inout Int) -> () end_access %7 : $*Int end_access %6 : $*Int end_access %5: $*Int destroy_value %2 : ${ var Int } %9 = tuple () return %9 : $() } // CHECK-LABEL: sil hidden [ossa] @twoSequentialInouts sil hidden [ossa] @twoSequentialInouts : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @takesOneInout : $@convention(thin) (@inout Int) -> () %5 = begin_access [modify] [unknown] %3 : $*Int %6 = apply %4(%5) : $@convention(thin) (@inout Int) -> () end_access %5 : $*Int %7 = begin_access [modify] [unknown] %3 : $*Int // no-error %8 = apply %4(%7) : $@convention(thin) (@inout Int) -> () end_access %7: $*Int destroy_value %2 : ${ var Int } %9 = tuple () return %8 : $() } // CHECK-LABEL: sil hidden [ossa] @unconditionalBranch sil hidden [ossa] @unconditionalBranch : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = begin_access [modify] [unknown] %3 : $*Int br finish finish: end_access %4: $*Int destroy_value %2 : ${ var Int } %5 = tuple () return %5 : $() } // CHECK-LABEL: sil hidden [ossa] @diamondMergeStacks sil hidden [ossa] @diamondMergeStacks : $@convention(thin) (Int, Builtin.Int1) -> () { bb0(%0 : $Int, %1 : $Builtin.Int1): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = begin_access [modify] [unknown] %3 : $*Int cond_br %1, then, else then: br finish else: br finish finish: end_access %4: $*Int destroy_value %2 : ${ var Int } %5 = tuple () return %5 : $() } // CHECK-LABEL: sil hidden [ossa] @loopMergeStacks sil hidden [ossa] @loopMergeStacks : $@convention(thin) (Int, Builtin.Int1) -> () { bb0(%0 : $Int, %1 : $Builtin.Int1): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = begin_access [modify] [unknown] %3 : $*Int br bb1 bb1: cond_br %1, bb1, bb2 bb2: end_access %4: $*Int destroy_value %2 : ${ var Int } %5 = tuple () return %5 : $() } // CHECK-LABEL: sil hidden [ossa] @loopWithError sil hidden [ossa] @loopWithError : $@convention(thin) (Int, Builtin.Int1) -> () { bb0(%0 : $Int, %1 : $Builtin.Int1): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int br bb1 bb1: // Make sure we don't diagnose twice. %4 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %5 = begin_access [modify] [unknown] %3 : $*Int // expected-note {{conflicting access is here}} end_access %5: $*Int end_access %4: $*Int cond_br %1, bb1, bb2 bb2: destroy_value %2 : ${ var Int } %6 = tuple () return %6 : $() } // CHECK-LABEL: sil hidden [ossa] @modifySubAccessesAreAllowed sil hidden [ossa] @modifySubAccessesAreAllowed : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () %5 = begin_access [modify] [unknown] %3 : $*Int %6 = begin_access [modify] [unknown] %5 : $*Int // no-error %7 = apply %4(%5, %6) : $@convention(thin) (@inout Int, @inout Int) -> () end_access %6 : $*Int end_access %5: $*Int destroy_value %2 : ${ var Int } %8 = tuple () return %8 : $() } // Multiple access kinds // CHECK-LABEL: sil hidden [ossa] @twoLocalReadsSimpleAliasing sil hidden [ossa] @twoLocalReadsSimpleAliasing : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %1 = alloc_box ${ var Int } %2 = project_box %1 : ${ var Int }, 0 store %0 to [trivial] %2 : $*Int %4 = begin_access [read] [unknown] %2 : $*Int %5 = begin_access [read] [unknown] %2 : $*Int // no-error end_access %5 : $*Int end_access %4: $*Int destroy_value %1 : ${ var Int } %6 = tuple () return %6 : $() } // CHECK-LABEL: sil hidden [ossa] @localReadFollowedByModify sil hidden [ossa] @localReadFollowedByModify : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %1 = alloc_box ${ var Int } %2 = project_box %1 : ${ var Int }, 0 store %0 to [trivial] %2 : $*Int %4 = begin_access [read] [unknown] %2 : $*Int // expected-note {{conflicting access is here}} %5 = begin_access [modify] [unknown] %2 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} end_access %5 : $*Int end_access %4: $*Int destroy_value %1 : ${ var Int } %6 = tuple () return %6 : $() } // CHECK-LABEL: sil hidden [ossa] @localModifyFollowedByRead sil hidden [ossa] @localModifyFollowedByRead : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %1 = alloc_box ${ var Int } %2 = project_box %1 : ${ var Int }, 0 store %0 to [trivial] %2 : $*Int %4 = begin_access [modify] [unknown] %2 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %5 = begin_access [read] [unknown] %2 : $*Int // expected-note {{conflicting access is here}} end_access %5 : $*Int end_access %4: $*Int destroy_value %1 : ${ var Int } %6 = tuple () return %6 : $() } class ClassWithStoredProperty { @_hasStorage var f: Int init() } // CHECK-LABEL: sil hidden [ossa] @classStoredProperty sil hidden [ossa] @classStoredProperty : $@convention(thin) (@owned ClassWithStoredProperty) -> () { bb0(%0 : @owned $ClassWithStoredProperty): %0a = begin_borrow %0 : $ClassWithStoredProperty %1 = ref_element_addr %0a : $ClassWithStoredProperty, #ClassWithStoredProperty.f // expected-error@+1{{overlapping accesses to 'f', but modification requires exclusive access; consider copying to a local variable}} %2 = begin_access [modify] [dynamic] %1 : $*Int %3 = ref_element_addr %0a : $ClassWithStoredProperty, #ClassWithStoredProperty.f // expected-note@+1{{conflicting access is here}} %4 = begin_access [modify] [dynamic] %3 : $*Int end_access %4 : $*Int end_access %2 : $*Int end_borrow %0a : $ClassWithStoredProperty destroy_value %0 : $ClassWithStoredProperty %5 = tuple () return %5 : $() } // CHECK-LABEL: sil hidden [ossa] @lookThroughBeginBorrow sil hidden [ossa] @lookThroughBeginBorrow : $@convention(thin) (@owned ClassWithStoredProperty) -> () { bb0(%0 : @owned $ClassWithStoredProperty): %1 = begin_borrow %0 : $ClassWithStoredProperty %2 = begin_borrow %0 : $ClassWithStoredProperty %3 = ref_element_addr %1 : $ClassWithStoredProperty, #ClassWithStoredProperty.f // expected-error@+1{{overlapping accesses to 'f', but modification requires exclusive access; consider copying to a local variable}} %4 = begin_access [modify] [dynamic] %3 : $*Int %5 = ref_element_addr %2 : $ClassWithStoredProperty, #ClassWithStoredProperty.f // expected-note@+1{{conflicting access is here}} %6 = begin_access [modify] [dynamic] %5 : $*Int end_access %6 : $*Int end_access %4 : $*Int end_borrow %2 : $ClassWithStoredProperty end_borrow %1 : $ClassWithStoredProperty destroy_value %0 : $ClassWithStoredProperty %7 = tuple () return %7 : $() } // Tests for address identity // Treat 'alloc_box' as identity for project_box // CHECK-LABEL: sil hidden [ossa] @twoAllocBoxProjections sil hidden [ossa] @twoAllocBoxProjections : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = copy_value %2 : ${ var Int } %5 = project_box %4 : ${ var Int }, 0 %6 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %7 = begin_access [modify] [unknown] %5 : $*Int // expected-note {{conflicting access is here}} end_access %7 : $*Int end_access %6: $*Int destroy_value %2 : ${ var Int } destroy_value %4 : ${ var Int } %8 = tuple () return %8 : $() } // CHECK-LABEL: sil hidden [ossa] @lookThroughMarkUninitialized sil hidden [ossa] @lookThroughMarkUninitialized : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %1 = alloc_box ${ var Int } %2 = mark_uninitialized [rootself] %1 : ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = project_box %2 : ${ var Int }, 0 %5 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %6 = begin_access [modify] [unknown] %4 : $*Int // expected-note {{conflicting access is here}} end_access %6 : $*Int end_access %5: $*Int destroy_value %2 : ${ var Int } %7 = tuple () return %7 : $() } // Treat global as identity for global_addr instruction- sil_global hidden @global1 : $Int sil_global hidden @global2 : $Int // CHECK-LABEL: sil hidden [ossa] @modifySameGlobal sil hidden [ossa] @modifySameGlobal : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %1 = global_addr @global1 :$*Int %2 = global_addr @global1 :$*Int %3 = begin_access [modify] [unknown] %1 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %4 = begin_access [modify] [unknown] %2 : $*Int // expected-note {{conflicting access is here}} end_access %4 : $*Int end_access %3: $*Int %5 = tuple () return %5 : $() } // CHECK-LABEL: sil hidden [ossa] @modifyDifferentGlobal sil hidden [ossa] @modifyDifferentGlobal : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %1 = global_addr @global1 :$*Int %2 = global_addr @global2 :$*Int %3 = begin_access [modify] [unknown] %1 : $*Int %4 = begin_access [modify] [unknown] %2 : $*Int // no-error end_access %4 : $*Int end_access %3: $*Int %5 = tuple () return %5 : $() } // Multiple errors accessing the same location // If we have a sequence of begin read - begin write - begin read accesses make // sure the second read doesn't report a confusing read-read conflict. // CHECK-LABEL: sil hidden [ossa] @readWriteReadConflictingThirdAccess sil hidden [ossa] @readWriteReadConflictingThirdAccess : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () %5 = begin_access [read] [unknown] %3 : $*Int // expected-note {{conflicting access is here}} %6 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %7 = begin_access [read] [unknown] %3 : $*Int // no-error %8 = apply %4(%5, %6) : $@convention(thin) (@inout Int, @inout Int) -> () end_access %7 : $*Int end_access %6 : $*Int end_access %5: $*Int destroy_value %2 : ${ var Int } %9 = tuple () return %9 : $() } // If we have a sequence of begin write - begin write - begin write accesses make sure the // third write doesn't report a conflict. // CHECK-LABEL: sil hidden [ossa] @writeWriteWriteConflictingThirdAccess sil hidden [ossa] @writeWriteWriteConflictingThirdAccess : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () %5 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %6 = begin_access [modify] [unknown] %3 : $*Int // expected-note {{conflicting access is here}} %7 = begin_access [modify] [unknown] %3 : $*Int // no-error %8 = apply %4(%5, %6) : $@convention(thin) (@inout Int, @inout Int) -> () end_access %7 : $*Int end_access %6 : $*Int end_access %5: $*Int destroy_value %2 : ${ var Int } %9 = tuple () return %9 : $() } // If we have a sequence of begin write - end write - begin write - begin write // accesses make sure the it is the second begin write that gets the note // about the conflict and not the first // CHECK-LABEL: sil hidden [ossa] @resetFirstAccessForNote sil hidden [ossa] @resetFirstAccessForNote : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () %5 = begin_access [modify] [unknown] %3 : $*Int // no-note end_access %5 : $*Int %6 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %7 = begin_access [modify] [unknown] %3 : $*Int // expected-note {{conflicting access is here}} %8 = apply %4(%5, %6) : $@convention(thin) (@inout Int, @inout Int) -> () end_access %7 : $*Int end_access %6: $*Int destroy_value %2 : ${ var Int } %9 = tuple () return %9 : $() } // Check for iterator invalidation issues when the hash from // basic blocks to analysis state is re-hashed. The number of // blocks in this test is determined by the initial size of the // 'BlockOutAccesses' DenseMap in the implementation. // // The unreachable block below must branch to bN where // N = 3/4 * INITIAL_SIZE - 2 sil [ossa] @blockMapRehash : $@convention(method) (Builtin.Int1) -> () { bb0(%0 : $Builtin.Int1): br bb1 bb1: br bb2 bb2: br bb3 bb3: br bb4 bb4: br bb5 bb5: br bb6 bb6: br bb7 bb7: br bb8 bb8: br bb9 bb9: br bb10 bb10: br bb11 bb11: br bb12 bb12: br bb13 bb13: br bb14 bb14: br bb15 bb15: br bb16 bb16: br bb17 bb17: br bb18 bb18: br bb19 bb19: br bb20 bb20: br bb21 bb21: br bb22 bb22: br bb23 // no-crash bb23: %1 = tuple () return %1 : $() bbUnreachable: br bb22 } // Check that a pointer_to_address access passes diagnostics. // // CHECK-LABEL: sil hidden [ossa] @pointerToAddr sil hidden [ossa] @pointerToAddr : $@convention(thin) (Builtin.RawPointer) -> Int { bb0(%0: $Builtin.RawPointer): %adr = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*Int %access = begin_access [read] [dynamic] %adr : $*Int %val = load [trivial] %access : $*Int end_access %access : $*Int return %val : $Int } // Helper. struct S { var x: Int } // Check inlined struct element access. // This only happens when mandatory passes are applied repeatedly. // e.g. importing from a debug standard library. // // CHECK-LABEL: sil hidden [ossa] @inlinedStructElement sil hidden [ossa] @inlinedStructElement : $@convention(thin) (@inout S) -> Int { bb0(%0 : $*S): %2 = begin_access [modify] [static] %0 : $*S %3 = struct_element_addr %2 : $*S, #S.x %4 = begin_access [read] [static] %3 : $*Int %5 = load [trivial] %4 : $*Int end_access %4 : $*Int end_access %2 : $*S return %5 : $Int } // Check inlined tuple element access. // This only happens when mandatory passes are applied repeatedly. // // CHECK-LABEL: sil hidden [ossa] @inlinedTupleElement sil hidden [ossa] @inlinedTupleElement : $@convention(thin) (@inout (Int, Int)) -> Int { bb0(%0 : $*(Int, Int)): %2 = begin_access [modify] [static] %0 : $*(Int, Int) %3 = tuple_element_addr %2 : $*(Int, Int), 0 %4 = begin_access [read] [static] %3 : $*Int %5 = load [trivial] %4 : $*Int end_access %4 : $*Int end_access %2 : $*(Int, Int) return %5 : $Int } // Check inlined enum access. // This only happens when mandatory passes are applied repeatedly. // // CHECK-LABEL: sil hidden [ossa] @inlinedEnumValue sil hidden [ossa] @inlinedEnumValue : $@convention(thin) (Int) -> (@out Optional, Int) { bb0(%0 : $*Optional, %1 : $Int): %6 = unchecked_take_enum_data_addr %0 : $*Optional, #Optional.some!enumelt.1 %7 = begin_access [read] [static] %6 : $*Int %8 = load [trivial] %7 : $*Int end_access %7 : $*Int return %8 : $Int } // Helper. class Storage {} // Check inlined array access. // This only happens when mandatory passes are applied repeatedly. // // CHECK-LABEL: sil hidden [ossa] @inlinedArrayProp sil hidden [ossa] @inlinedArrayProp : $@convention(thin) (@guaranteed Storage, Builtin.Word) -> Int { bb0(%0 : @guaranteed $Storage, %1 : $Builtin.Word): %2 = ref_tail_addr %0 : $Storage, $UInt %3 = begin_access [read] [static] %2 : $*UInt %4 = tail_addr %3 : $*UInt, %1 : $Builtin.Word, $Int %5 = begin_access [read] [static] %4 : $*Int %6 = index_addr %5 : $*Int, %1 : $Builtin.Word %7 = begin_access [read] [static] %6 : $*Int %8 = load [trivial] %7 : $*Int end_access %7 : $*Int end_access %5 : $*Int end_access %3 : $*UInt return %8 : $Int } // Conflicts involving noescape closures. sil hidden [ossa] @closureThatModifiesCaptureAndTakesInout: $@convention(thin) (@inout Int, @inout_aliasable Int) -> () { bb0(%0 : $*Int, %1 : $*Int): %2 = begin_access [modify] [unknown] %1 : $*Int // expected-note {{conflicting access is here}} end_access %2 : $*Int %3 = tuple () return %3 : $() } // FIXME: We should prevent SILGen from emitting such a thing because the only // way to distinguish a capture from a regular @inout argument here is by the // @inout_aliasable convention. When we eliminate that convention we should make // sure that whenever we promote a closure from a @callee_guaranteed @noescape // function type to @conventio(thin), we insert access enforcement on the caller // side for captured variables. // // CHECK-LABEL: sil hidden [ossa] @inProgressModifyModifyConflictWithCallToClosure sil hidden [ossa] @inProgressModifyModifyConflictWithCallToClosure : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @closureThatModifiesCaptureAndTakesInout: $@convention(thin) (@inout Int, @inout_aliasable Int) -> () %5 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %8 = apply %4(%5, %3) : $@convention(thin) (@inout Int, @inout_aliasable Int) -> () end_access %5: $*Int destroy_value %2 : ${ var Int } %9 = tuple () return %9 : $() } sil hidden [ossa] @closureWithArgument_1 : $@convention(thin) (Int, @inout_aliasable Int) -> () { bb0(%0 : $Int, %1 : $*Int): %2 = begin_access [modify] [unknown] %1 : $*Int // expected-note 2 {{conflicting access is here}} end_access %2 : $*Int %3 = tuple () return %3 : $() } // CHECK-LABEL: sil hidden [ossa] @inProgressConflictWithNoEscapeClosureArgument sil hidden [ossa] @inProgressConflictWithNoEscapeClosureArgument : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @takesInoutAndNoEscapeClosureTakingArgument : $@convention(thin) (@inout Int, @noescape @owned @callee_owned (Int) -> ()) -> () %5 = function_ref @closureWithArgument_1 : $@convention(thin) (Int, @inout_aliasable Int) -> () %6 = partial_apply %5(%3) : $@convention(thin) (Int, @inout_aliasable Int) -> () %conv = convert_escape_to_noescape %6 : $@callee_owned (Int) -> () to $@callee_owned @noescape (Int) -> () %7 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %8 = apply %4(%7, %conv) : $@convention(thin) (@inout Int, @noescape @owned @callee_owned (Int) -> ()) -> () end_access %7: $*Int destroy_value %6 : $@callee_owned (Int) -> () destroy_value %2 : ${ var Int } %9 = tuple () return %9 : $() } // CHECK-LABEL: sil hidden [ossa] @inProgressConflictWithNoEscapeGuaranteedClosureArgument : $@convention(thin) (Int) -> () { sil hidden [ossa] @inProgressConflictWithNoEscapeGuaranteedClosureArgument : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @takesInoutAndNoEscapeGuaranteedClosureTakingArgument : $@convention(thin) (@inout Int, @noescape @guaranteed @callee_guaranteed (Int) -> ()) -> () %5 = function_ref @closureWithArgument_1 : $@convention(thin) (Int, @inout_aliasable Int) -> () %6 = partial_apply [callee_guaranteed] %5(%3) : $@convention(thin) (Int, @inout_aliasable Int) -> () %conv = convert_escape_to_noescape %6 : $@callee_guaranteed (Int) -> () to $@callee_guaranteed @noescape (Int) -> () %bconv = begin_borrow %conv : $@callee_guaranteed @noescape (Int) -> () %7 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %8 = apply %4(%7, %bconv) : $@convention(thin) (@inout Int, @noescape @guaranteed @callee_guaranteed (Int) -> ()) -> () end_access %7: $*Int end_borrow %bconv : $@callee_guaranteed @noescape (Int) -> () destroy_value %6 : $@callee_guaranteed (Int) -> () destroy_value %2 : ${ var Int } %9 = tuple () return %9 : $() } sil hidden [ossa] @closureThatModifiesCapture_2 : $@convention(thin) (@inout_aliasable Int) -> () { bb0(%0 : $*Int): %1 = begin_access [modify] [unknown] %0 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} end_access %1 : $*Int %2 = tuple () return %2 : $() } // CHECK-LABEL: sil hidden [ossa] @inProgressReadModifyConflictWithNoEscapeClosureArgument sil hidden [ossa] @inProgressReadModifyConflictWithNoEscapeClosureArgument : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @takesInoutAndNoEscapeClosure : $@convention(thin) (@inout Int, @noescape @owned @callee_owned () -> ()) -> () %5 = function_ref @closureThatModifiesCapture_2 : $@convention(thin) (@inout_aliasable Int) -> () %6 = partial_apply %5(%3) : $@convention(thin) (@inout_aliasable Int) -> () %conv = convert_escape_to_noescape %6 : $@callee_owned () -> () to $@callee_owned @noescape () -> () %7 = begin_access [read] [unknown] %3 : $*Int // expected-note {{conflicting access is here}} %8 = apply %4(%3, %conv) : $@convention(thin) (@inout Int, @noescape @owned @callee_owned () -> ()) -> () end_access %7: $*Int destroy_value %6 : $@callee_owned () -> () destroy_value %2 : ${ var Int } %9 = tuple () return %9 : $() } sil hidden [ossa] @closureWithConcreteReturn : $@convention(thin) (Int, @inout_aliasable Int) -> (Int) { bb0(%0 : $Int, %1 : $*Int): %2 = begin_access [modify] [unknown] %1 : $*Int // expected-note {{conflicting access is here}} end_access %2 : $*Int return %0 : $Int } sil [reabstraction_thunk] @thunkForClosureWithConcreteReturn : $@convention(thin) (Int, @noescape @callee_owned (Int) -> Int) -> @out Int // CHECK-LABEL: sil hidden [ossa] @inProgressConflictWithNoEscapeClosureWithReabstractionThunk sil hidden [ossa] @inProgressConflictWithNoEscapeClosureWithReabstractionThunk : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @takesInoutAndNoEscapeClosureWithGenericReturn : $@convention(thin) (@inout Int, @noescape @callee_owned (Int) -> @out T_0) -> () %5 = function_ref @closureWithConcreteReturn : $@convention(thin) (Int, @inout_aliasable Int) -> (Int) %6 = partial_apply %5(%3) : $@convention(thin) (Int, @inout_aliasable Int) -> (Int) %7 = convert_escape_to_noescape %6 : $@callee_owned (Int) -> Int to $@noescape @callee_owned (Int) -> Int %8 = function_ref @thunkForClosureWithConcreteReturn : $@convention(thin) (Int, @noescape @callee_owned (Int) -> Int) -> @out Int %9 = partial_apply %8(%7) : $@convention(thin) (Int, @noescape @callee_owned (Int) -> Int) -> @out Int %10 = convert_escape_to_noescape %9 : $@callee_owned (Int) -> @out Int to $@noescape @callee_owned (Int) -> @out Int %11 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %12 = apply %4(%11, %10) : $@convention(thin) (@inout Int, @noescape @callee_owned (Int) -> @out T_0) -> () end_access %11: $*Int destroy_value %9 : $@callee_owned (Int) -> @out Int destroy_value %6 : $@callee_owned (Int) -> Int destroy_value %2 : ${ var Int } %13 = tuple () return %13 : $() } sil [reabstraction_thunk] @thunkForCalleeGuaranteed : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> () sil [reabstraction_thunk] @withoutActuallyEscapingThunk : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> () sil hidden [ossa] @closureThatModifiesCapture_1: $@convention(thin) (@inout_aliasable Int) -> () { bb0(%0 : $*Int): %1 = begin_access [modify] [unknown] %0 : $*Int // expected-note 3{{conflicting access is here}} end_access %1 : $*Int %2 = tuple () return %2 : $() } // CHECK-LABEL: sil hidden [ossa] @inProgressConflictWithNoEscapeClosureWithBlockStorage sil hidden [ossa] @inProgressConflictWithNoEscapeClosureWithBlockStorage : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @takesInoutAndNoEscapeBlockClosure : $@convention(thin) (@inout Int, @owned @convention(block) @noescape () -> ()) -> () %5 = function_ref @closureThatModifiesCapture_1 : $@convention(thin) (@inout_aliasable Int) -> () %6 = partial_apply [callee_guaranteed] %5(%3) : $@convention(thin) (@inout_aliasable Int) -> () %conv = convert_escape_to_noescape %6 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> () %thunk = function_ref @withoutActuallyEscapingThunk : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> () %sentinel = partial_apply [callee_guaranteed] %thunk(%conv) : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> () %sentinel2 = mark_dependence %sentinel : $@callee_guaranteed () -> () on %conv : $@noescape @callee_guaranteed () -> () %sentinel3 = copy_value %sentinel2 : $@callee_guaranteed () -> () %7 = alloc_stack $@block_storage @callee_guaranteed () -> () %8 = project_block_storage %7 : $*@block_storage @callee_guaranteed () -> () store %sentinel3 to [init] %8 : $*@callee_guaranteed () -> () %10 = function_ref @thunkForCalleeGuaranteed : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> () %11 = init_block_storage_header %7 : $*@block_storage @callee_guaranteed () -> (), invoke %10 : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> (), type $@convention(block) @noescape () -> () %12 = copy_block %11 : $@convention(block) @noescape () -> () %13 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %14 = apply %4(%13, %12) : $@convention(thin) (@inout Int, @owned @convention(block) @noescape () -> ()) -> () end_access %13 : $*Int destroy_addr %8 : $*@callee_guaranteed() -> () dealloc_stack %7 : $*@block_storage @callee_guaranteed () -> () destroy_value %sentinel2 : $@callee_guaranteed () -> () destroy_value %6 : $@callee_guaranteed () -> () destroy_value %2 : ${ var Int } %ret = tuple () return %ret : $() } // CHECK-LABEL: sil hidden [ossa] @inProgressConflictWithNoEscapeClosureWithOptionalBlockStorage sil hidden [ossa] @inProgressConflictWithNoEscapeClosureWithOptionalBlockStorage : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @takesInoutAndNoEscapeOptionalBlockClosure : $@convention(thin) (@inout Int, @owned Optional<@convention(block) @noescape () -> ()>) -> () %5 = function_ref @closureThatModifiesCapture_1 : $@convention(thin) (@inout_aliasable Int) -> () %6 = partial_apply [callee_guaranteed] %5(%3) : $@convention(thin) (@inout_aliasable Int) -> () %conv = convert_escape_to_noescape %6 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> () %thunk = function_ref @withoutActuallyEscapingThunk : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> () %sentinel = partial_apply [callee_guaranteed] %thunk(%conv) : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> () %sentinel2 = mark_dependence %sentinel : $@callee_guaranteed () -> () on %conv : $@noescape @callee_guaranteed () -> () %sentinel3 = copy_value %sentinel2 : $@callee_guaranteed () -> () %7 = alloc_stack $@block_storage @callee_guaranteed () -> () %8 = project_block_storage %7 : $*@block_storage @callee_guaranteed () -> () store %sentinel3 to [init] %8 : $*@callee_guaranteed () -> () %10 = function_ref @thunkForCalleeGuaranteed : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> () %11 = init_block_storage_header %7 : $*@block_storage @callee_guaranteed () -> (), invoke %10 : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> (), type $@convention(block) @noescape () -> () %12 = copy_block %11 : $@convention(block) @noescape () -> () %13 = enum $Optional<@convention(block) @noescape () -> ()>, #Optional.some!enumelt.1, %12 : $@convention(block) @noescape () -> () %14 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %15 = apply %4(%14, %13) : $@convention(thin) (@inout Int, @owned Optional<@convention(block) @noescape () -> ()>) -> () end_access %14 : $*Int destroy_addr %8 : $*@callee_guaranteed () -> () dealloc_stack %7 : $*@block_storage @callee_guaranteed () -> () destroy_value %sentinel2 : $@callee_guaranteed () -> () destroy_value %6 : $@callee_guaranteed () -> () destroy_value %2 : ${ var Int } %ret = tuple () return %ret : $() } // Stored property relaxation. struct NestedStructWithStoredProps { var a: Int var b: Int } struct StructWithStoredProps { var x: Int var y: Int var n: NestedStructWithStoredProps } sil [ossa] @takesInoutIntAndStructWithStoredProps : $@convention(thin) (@inout Int, @inout StructWithStoredProps) -> () // CHECK-LABEL: sil hidden [ossa] @twoSeparateStoredProperties sil hidden [ossa] @twoSeparateStoredProperties : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var StructWithStoredProps } %3 = project_box %2 : ${ var StructWithStoredProps }, 0 %4 = function_ref @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () %5 = begin_access [modify] [unknown] %3 : $*StructWithStoredProps %6 = struct_element_addr %5 : $*StructWithStoredProps, #StructWithStoredProps.x %7 = begin_access [modify] [unknown] %3 : $*StructWithStoredProps // no-error %8 = struct_element_addr %7 : $*StructWithStoredProps, #StructWithStoredProps.y %9 = apply %4(%6, %8) : $@convention(thin) (@inout Int, @inout Int) -> () end_access %7 : $*StructWithStoredProps end_access %5: $*StructWithStoredProps destroy_value %2 : ${ var StructWithStoredProps } %10 = tuple () return %10 : $() } // CHECK-LABEL: sil hidden [ossa] @twoSeparateNestedStoredProperties sil hidden [ossa] @twoSeparateNestedStoredProperties : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var StructWithStoredProps } %3 = project_box %2 : ${ var StructWithStoredProps }, 0 %4 = function_ref @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () %5 = begin_access [modify] [unknown] %3 : $*StructWithStoredProps %6 = struct_element_addr %5 : $*StructWithStoredProps, #StructWithStoredProps.n %7 = struct_element_addr %6 : $*NestedStructWithStoredProps, #NestedStructWithStoredProps.a %8 = begin_access [modify] [unknown] %3 : $*StructWithStoredProps // no-error %9 = struct_element_addr %8 : $*StructWithStoredProps, #StructWithStoredProps.n %10 = struct_element_addr %9 : $*NestedStructWithStoredProps, #NestedStructWithStoredProps.b %11 = apply %4(%7, %10) : $@convention(thin) (@inout Int, @inout Int) -> () end_access %8 : $*StructWithStoredProps end_access %5: $*StructWithStoredProps destroy_value %2 : ${ var StructWithStoredProps } %12 = tuple () return %12 : $() } // CHECK-LABEL: sil hidden [ossa] @theSameStoredProperty sil hidden [ossa] @theSameStoredProperty : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var StructWithStoredProps } %3 = project_box %2 : ${ var StructWithStoredProps }, 0 %4 = function_ref @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () %5 = begin_access [modify] [unknown] %3 : $*StructWithStoredProps // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %6 = struct_element_addr %5 : $*StructWithStoredProps, #StructWithStoredProps.x %7 = begin_access [modify] [unknown] %3 : $*StructWithStoredProps // expected-note {{conflicting access is here}} %8 = struct_element_addr %7 : $*StructWithStoredProps, #StructWithStoredProps.x %9 = apply %4(%6, %8) : $@convention(thin) (@inout Int, @inout Int) -> () end_access %7 : $*StructWithStoredProps end_access %5: $*StructWithStoredProps destroy_value %2 : ${ var StructWithStoredProps } %10 = tuple () return %10 : $() } // CHECK-LABEL: sil hidden [ossa] @storedPropertyAndAggregate sil hidden [ossa] @storedPropertyAndAggregate : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var StructWithStoredProps } %3 = project_box %2 : ${ var StructWithStoredProps }, 0 %4 = function_ref @takesInoutIntAndStructWithStoredProps : $@convention(thin) (@inout Int, @inout StructWithStoredProps) -> () %5 = begin_access [modify] [unknown] %3 : $*StructWithStoredProps // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %6 = struct_element_addr %5 : $*StructWithStoredProps, #StructWithStoredProps.x %7 = begin_access [modify] [unknown] %3 : $*StructWithStoredProps // expected-note {{conflicting access is here}} %8 = apply %4(%6, %7) : $@convention(thin) (@inout Int, @inout StructWithStoredProps) -> () end_access %7 : $*StructWithStoredProps end_access %5: $*StructWithStoredProps destroy_value %2 : ${ var StructWithStoredProps } %9 = tuple () return %9 : $() } // CHECK-LABEL: sil hidden [ossa] @nestedStoredPropertyAndAggregate sil hidden [ossa] @nestedStoredPropertyAndAggregate : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var StructWithStoredProps } %3 = project_box %2 : ${ var StructWithStoredProps }, 0 %4 = function_ref @takesInoutIntAndStructWithStoredProps : $@convention(thin) (@inout Int, @inout StructWithStoredProps) -> () %5 = begin_access [modify] [unknown] %3 : $*StructWithStoredProps // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %6 = struct_element_addr %5 : $*StructWithStoredProps, #StructWithStoredProps.n %7 = struct_element_addr %6 : $*NestedStructWithStoredProps, #NestedStructWithStoredProps.a %8 = begin_access [modify] [unknown] %3 : $*StructWithStoredProps // expected-note {{conflicting access is here}} %9 = struct_element_addr %8 : $*StructWithStoredProps, #StructWithStoredProps.n end_access %8 : $*StructWithStoredProps end_access %5: $*StructWithStoredProps destroy_value %2 : ${ var StructWithStoredProps } %10 = tuple () return %10 : $() } // CHECK-LABEL: sil hidden [ossa] @twoSeparateTupleElements sil hidden [ossa] @twoSeparateTupleElements : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var (Int, Int) } %3 = project_box %2 : ${ var (Int, Int) }, 0 %4 = function_ref @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () %5 = begin_access [modify] [unknown] %3 : $*(Int, Int) %6 = tuple_element_addr %5 : $*(Int, Int), 0 %7 = begin_access [modify] [unknown] %3 : $*(Int, Int) // no-error %8 = tuple_element_addr %7 : $*(Int, Int), 1 %9 = apply %4(%6, %8) : $@convention(thin) (@inout Int, @inout Int) -> () end_access %7 : $*(Int, Int) end_access %5: $*(Int, Int) destroy_value %2 : ${ var (Int, Int) } %10 = tuple () return %10 : $() } // CHECK-LABEL: sil hidden [ossa] @sameTupleElement sil hidden [ossa] @sameTupleElement : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var (Int, Int) } %3 = project_box %2 : ${ var (Int, Int) }, 0 %4 = function_ref @takesTwoInouts : $@convention(thin) (@inout Int, @inout Int) -> () %5 = begin_access [modify] [unknown] %3 : $*(Int, Int) // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %6 = tuple_element_addr %5 : $*(Int, Int), 0 %7 = begin_access [modify] [unknown] %3 : $*(Int, Int) // expected-note {{conflicting access is here}} %8 = tuple_element_addr %7 : $*(Int, Int), 0 %9 = apply %4(%6, %8) : $@convention(thin) (@inout Int, @inout Int) -> () end_access %7 : $*(Int, Int) end_access %5: $*(Int, Int) destroy_value %2 : ${ var (Int, Int) } %10 = tuple () return %10 : $() } sil hidden [ossa] @passNilToNoEscape : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 %4 = enum $Optional<@convention(block) @noescape () -> ()>, #Optional.none!enumelt %5 = function_ref @takesInoutAndNoEscapeOptionalBlockClosure : $@convention(thin) (@inout Int, @owned Optional<@convention(block) @noescape () -> ()>) -> () %6 = apply %5(%3, %4) : $@convention(thin) (@inout Int, @owned Optional<@convention(block) @noescape () -> ()>) -> () destroy_value %2 : ${ var Int } %7 = tuple () return %7 : $() } sil private [ossa] @closureForDirectPartialApplyTakingInout : $@convention(thin) (@inout Int, @inout_aliasable Int) -> () { bb0(%0 : $*Int, %1 : $*Int): %access = begin_access [read] [unknown] %1 : $*Int // expected-note 3 {{conflicting access is here}} %val = load [trivial] %access : $*Int end_access %access : $*Int %v = tuple () return %v : $() } sil hidden [ossa] @directPartialApplyTakingInout : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %box = alloc_box ${ var Int }, var, name "i" %boxadr = project_box %box : ${ var Int }, 0 store %0 to [trivial] %boxadr : $*Int %f = function_ref @closureForDirectPartialApplyTakingInout : $@convention(thin) (@inout Int, @inout_aliasable Int) -> () %pa = partial_apply [callee_guaranteed] %f(%boxadr) : $@convention(thin) (@inout Int, @inout_aliasable Int) -> () %nepa = convert_escape_to_noescape %pa : $@callee_guaranteed (@inout Int) -> () to $@noescape @callee_guaranteed (@inout Int) -> () %access = begin_access [modify] [unknown] %boxadr : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %call = apply %nepa(%access) : $@noescape @callee_guaranteed (@inout Int) -> () end_access %access : $*Int destroy_value %pa : $@callee_guaranteed (@inout Int) -> () destroy_value %box : ${ var Int } %v = tuple () return %v : $() } sil private [ossa] @closureForDirectPartialApplyChain : $@convention(thin) (@inout Int, Int, @inout_aliasable Int) -> () { bb0(%0 : $*Int, %1 : $Int, %2 : $*Int): %access = begin_access [read] [unknown] %2 : $*Int // expected-note {{conflicting access is here}} %val = load [trivial] %access : $*Int end_access %access : $*Int %v = tuple () return %v : $() } sil hidden [ossa] @directPartialApplyChain : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %box = alloc_box ${ var Int }, var, name "i" %boxadr = project_box %box : ${ var Int }, 0 store %0 to [trivial] %boxadr : $*Int %f = function_ref @closureForDirectPartialApplyChain : $@convention(thin) (@inout Int, Int, @inout_aliasable Int) -> () %pa1 = partial_apply [callee_guaranteed] %f(%boxadr) : $@convention(thin) (@inout Int, Int, @inout_aliasable Int) -> () %pa2 = partial_apply [callee_guaranteed] %pa1(%0) : $@callee_guaranteed (@inout Int, Int) -> () %nepa = convert_escape_to_noescape %pa2 : $@callee_guaranteed (@inout Int) -> () to $@noescape @callee_guaranteed (@inout Int) -> () %access = begin_access [modify] [unknown] %boxadr : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %call = apply %nepa(%access) : $@noescape @callee_guaranteed (@inout Int) -> () end_access %access : $*Int destroy_value %pa2 : $@callee_guaranteed (@inout Int) -> () destroy_value %box : ${ var Int } %v = tuple () return %v : $() } sil private [ossa] @closureForPartialApplyArgChain : $@convention(thin) (Int, @inout_aliasable Int) -> () { bb0(%0 : $Int, %1 : $*Int): %access = begin_access [read] [unknown] %1 : $*Int // expected-note {{conflicting access is here}} %val = load [trivial] %access : $*Int end_access %access : $*Int %v = tuple () return %v : $() } sil hidden [ossa] @partialApplyArgChain : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = alloc_box ${ var Int } %3 = project_box %2 : ${ var Int }, 0 store %0 to [trivial] %3 : $*Int %4 = function_ref @takesInoutAndNoEscapeClosure : $@convention(thin) (@inout Int, @noescape @owned @callee_owned () -> ()) -> () %5 = function_ref @closureForPartialApplyArgChain : $@convention(thin) (Int, @inout_aliasable Int) -> () %6 = partial_apply %5(%3) : $@convention(thin) (Int, @inout_aliasable Int) -> () %7 = partial_apply %6(%0) : $@callee_owned (Int) -> () %conv = convert_escape_to_noescape %7 : $@callee_owned () -> () to $@callee_owned @noescape () -> () %8 = begin_access [modify] [unknown] %3 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %9 = apply %4(%8, %conv) : $@convention(thin) (@inout Int, @noescape @owned @callee_owned () -> ()) -> () end_access %8: $*Int destroy_value %7 : $@callee_owned () -> () destroy_value %2 : ${ var Int } %v = tuple () return %v : $() } class SomeClass {} // LLDB uses mark_uninitialized [var] in an unsupported way via an address to // pointer. LLDB shouldn't do this, but until it is removed and validated we // need a test for this. // // Check that this doesn't trigger the DiagnoseStaticExclusivity // assert that all accesses have a valid AccessedStorage source. sil [ossa] @lldb_unsupported_markuninitialized_testcase : $@convention(thin) (UnsafeMutablePointer) -> () { bb0(%0 : $UnsafeMutablePointer): %2 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue %3 = integer_literal $Builtin.Int64, 8 %4 = index_raw_pointer %2 : $Builtin.RawPointer, %3 : $Builtin.Int64 %5 = pointer_to_address %4 : $Builtin.RawPointer to [strict] $*Builtin.RawPointer %6 = load [trivial] %5 : $*Builtin.RawPointer %7 = pointer_to_address %6 : $Builtin.RawPointer to [strict] $*Error %8 = mark_uninitialized [var] %7 : $*Error %9 = struct_extract %0 : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue %10 = integer_literal $Builtin.Int64, 0 %11 = index_raw_pointer %9 : $Builtin.RawPointer, %10 : $Builtin.Int64 %12 = pointer_to_address %11 : $Builtin.RawPointer to [strict] $*Builtin.RawPointer %13 = load [trivial] %12 : $*Builtin.RawPointer %14 = pointer_to_address %13 : $Builtin.RawPointer to [strict] $*@thick SomeClass.Type %15 = mark_uninitialized [var] %14 : $*@thick SomeClass.Type %16 = metatype $@thick SomeClass.Type %17 = begin_access [modify] [unsafe] %15 : $*@thick SomeClass.Type assign %16 to %17 : $*@thick SomeClass.Type end_access %17 : $*@thick SomeClass.Type %9999 = tuple() return %9999 : $() } sil [ossa] @addressor : $@convention(thin) () -> UnsafeMutablePointer // An addressor produces an unsafely accessed RawPointer. Pass the // address to an inout can result in an enforced (unknown) access on // the unsafe address. We need to handle this case without asserting. sil [ossa] @addressorAccess : $@convention(thin) (@guaranteed ClassWithStoredProperty, Int32) -> () { bb0(%0 : @guaranteed $ClassWithStoredProperty, %1 : $Int32): %f = function_ref @addressor : $@convention(thin) () -> UnsafeMutablePointer %up = apply %f() : $@convention(thin) () -> UnsafeMutablePointer %o = unchecked_ref_cast %0 : $ClassWithStoredProperty to $Builtin.NativeObject %ptr = struct_extract %up : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue %adr = pointer_to_address %ptr : $Builtin.RawPointer to [strict] $*Int32 %dep = mark_dependence %adr : $*Int32 on %o : $Builtin.NativeObject %a1 = begin_access [modify] [unsafe] %dep : $*Int32 %a2 = begin_access [modify] [static] %a1 : $*Int32 store %1 to [trivial] %a2 : $*Int32 end_access %a2 : $*Int32 end_access %a1 : $*Int32 %v = tuple () return %v : $() } // [SR-8201]: Swift 4.2 Crash in DiagnoseStaticExclusivity (llvm_unreachable) // mutatingNoescapeWithThunk and mutatingNoescapeConflictWithThunk. // Test that noescape closure verification allows a closure with @inout_aliasable argument convention, // which may only be used as a nonescaping closure, can be passed to a reabstraction thunk // without the @noescape function-type argument convention. sil [ossa] @mutatingNoescapeHelper : $@convention(thin) (Optional>, @inout_aliasable Int32) -> () { bb0(%0 : $Optional>, %1 : $*Int32): %up = unchecked_enum_data %0 : $Optional>, #Optional.some!enumelt.1 %p = struct_extract %up : $UnsafePointer, #UnsafePointer._rawValue %adr = pointer_to_address %p : $Builtin.RawPointer to [strict] $*Int32 %val = load [trivial] %adr : $*Int32 %access = begin_access [modify] [unknown] %1 : $*Int32 // expected-note {{conflicting access is here}} store %val to [trivial] %access : $*Int32 end_access %access : $*Int32 %v = tuple () return %v : $() } sil shared [transparent] [serializable] [reabstraction_thunk] [ossa] @mutatingNoescapeThunk : $@convention(thin) (UnsafePointer, @guaranteed @callee_guaranteed (Optional>) -> ()) -> () { bb0(%0 : $UnsafePointer, %1 : @guaranteed $@callee_guaranteed (Optional>) -> ()): %e = enum $Optional>, #Optional.some!enumelt.1, %0 : $UnsafePointer %c = apply %1(%e) : $@callee_guaranteed (Optional>) -> () %v = tuple () return %v : $() } sil [ossa] @takeMutatingNoescapeClosure : $@convention(thin) <τ_0_0> (UnsafePointer<τ_0_0>, @noescape @callee_guaranteed (UnsafePointer) -> ()) -> () // A (mutating) closure taking an @inout_aliasable argument may be // passed to a reabstraction_thunk as an escaping function type. This // is valid as long as the thunk is only used as a @nosecape type. sil [ossa] @mutatingNoescapeWithThunk : $@convention(method) (UnsafePointer, @inout Int32) -> () { bb0(%0 : $UnsafePointer, %1 : $*Int32): %f1 = function_ref @mutatingNoescapeHelper : $@convention(thin) (Optional>, @inout_aliasable Int32) -> () %pa1 = partial_apply [callee_guaranteed] %f1(%1) : $@convention(thin) (Optional>, @inout_aliasable Int32) -> () %thunk = function_ref @mutatingNoescapeThunk : $@convention(thin) (UnsafePointer, @guaranteed @callee_guaranteed (Optional>) -> ()) -> () %pa2 = partial_apply [callee_guaranteed] %thunk(%pa1) : $@convention(thin) (UnsafePointer, @guaranteed @callee_guaranteed (Optional>) -> ()) -> () %closure = convert_escape_to_noescape [not_guaranteed] %pa2 : $@callee_guaranteed (UnsafePointer) -> () to $@noescape @callee_guaranteed (UnsafePointer) -> () %f2 = function_ref @takeMutatingNoescapeClosure : $@convention(thin) <τ_0_0> (UnsafePointer<τ_0_0>, @noescape @callee_guaranteed (UnsafePointer) -> ()) -> () %call = apply %f2(%0, %closure) : $@convention(thin) <τ_0_0> (UnsafePointer<τ_0_0>, @noescape @callee_guaranteed (UnsafePointer) -> ()) -> () destroy_value %pa2 : $@callee_guaranteed (UnsafePointer) -> () %v = tuple () return %v : $() } sil [ossa] @takeInoutAndMutatingNoescapeClosure : $@convention(thin) <τ_0_0> (@inout Int32, UnsafePointer<τ_0_0>, @noescape @callee_guaranteed (UnsafePointer) -> ()) -> () sil [ossa] @mutatingNoescapeConflictWithThunk : $@convention(method) (UnsafePointer, @inout Int32) -> () { bb0(%0 : $UnsafePointer, %1 : $*Int32): %f1 = function_ref @mutatingNoescapeHelper : $@convention(thin) (Optional>, @inout_aliasable Int32) -> () %pa1 = partial_apply [callee_guaranteed] %f1(%1) : $@convention(thin) (Optional>, @inout_aliasable Int32) -> () %thunk = function_ref @mutatingNoescapeThunk : $@convention(thin) (UnsafePointer, @guaranteed @callee_guaranteed (Optional>) -> ()) -> () %pa2 = partial_apply [callee_guaranteed] %thunk(%pa1) : $@convention(thin) (UnsafePointer, @guaranteed @callee_guaranteed (Optional>) -> ()) -> () %closure = convert_escape_to_noescape [not_guaranteed] %pa2 : $@callee_guaranteed (UnsafePointer) -> () to $@noescape @callee_guaranteed (UnsafePointer) -> () %f2 = function_ref @takeInoutAndMutatingNoescapeClosure : $@convention(thin) <τ_0_0> (@inout Int32, UnsafePointer<τ_0_0>, @noescape @callee_guaranteed (UnsafePointer) -> ()) -> () %access = begin_access [modify] [unknown] %1 : $*Int32 // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %call = apply %f2(%access, %0, %closure) : $@convention(thin) <τ_0_0> (@inout Int32, UnsafePointer<τ_0_0>, @noescape @callee_guaranteed (UnsafePointer) -> ()) -> () destroy_value %pa2 : $@callee_guaranteed (UnsafePointer) -> () end_access %access : $*Int32 %v = tuple () return %v : $() } // Swift CI (macOS release master, OSS): SIL verification failed: Unknown formal access pattern: storage // Test access marker verification of a KeyPath projection with nested @inout access after inlining. sil @takeInoutInt : $@convention(thin) (@inout Int) -> () // The compiler intrinsic _projectXXXKeyPath returns a tuple of // UnsafePointer and Optional AnyObject. After inlining, the unsafe // pointer may appear to originate from anywhere, including a phi. // There's currently no way to tell that the UnsafePointer originated // from a KeyPath projection. This pattern could occur with any // addressor. In either case, the nested access is valid but // unidentified. Addressors that require enforcement must start a // dynamic access within the addressor itself, before returning an // UnsafePointer. sil [ossa] @testNestedKeypathAccess : $@convention(thin) (@guaranteed (UnsafeMutablePointer, Optional)) -> () { bb0(%0 : @guaranteed $(UnsafeMutablePointer, Optional)): %up = tuple_extract %0 : $(UnsafeMutablePointer, Optional), 0 %o = tuple_extract %0 : $(UnsafeMutablePointer, Optional), 1 %p = struct_extract %up : $UnsafeMutablePointer, #UnsafeMutablePointer._rawValue %adr = pointer_to_address %p : $Builtin.RawPointer to [strict] $*Int %dep = mark_dependence %adr : $*Int on %o : $Optional %access = begin_access [modify] [static] %dep : $*Int %f = function_ref @takeInoutInt : $@convention(thin) (@inout Int) -> () %call = apply %f(%access) : $@convention(thin) (@inout Int) -> () end_access %access : $*Int %v = tuple () return %v : $() } // Test a conflict on a noescape closure where multiple closures may be conditionally stored in an ObjC block. sil hidden [ossa] @noEscapeClosureWithConditionalBlockStorage : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %box = alloc_box ${ var Int } %boxadr = project_box %box : ${ var Int }, 0 store %0 to [trivial] %boxadr : $*Int %closure = function_ref @closureThatModifiesCapture_1 : $@convention(thin) (@inout_aliasable Int) -> () %pa = partial_apply [callee_guaranteed] %closure(%boxadr) : $@convention(thin) (@inout_aliasable Int) -> () %conv = convert_escape_to_noescape %pa : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> () %thunk = function_ref @withoutActuallyEscapingThunk : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> () %sentinel = partial_apply [callee_guaranteed] %thunk(%conv) : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> () %sentinel2 = mark_dependence %sentinel : $@callee_guaranteed () -> () on %conv : $@noescape @callee_guaranteed () -> () %sentinel3 = copy_value %sentinel2 : $@callee_guaranteed () -> () %calleethunk = function_ref @thunkForCalleeGuaranteed : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> () cond_br undef, bb1, bb2 bb1: %bs1 = alloc_stack $@block_storage @callee_guaranteed () -> () %pbs1 = project_block_storage %bs1 : $*@block_storage @callee_guaranteed () -> () store %sentinel3 to [init] %pbs1 : $*@callee_guaranteed () -> () %initblock1 = init_block_storage_header %bs1 : $*@block_storage @callee_guaranteed () -> (), invoke %calleethunk : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> (), type $@convention(block) @noescape () -> () %copyblock1 = copy_block %initblock1 : $@convention(block) @noescape () -> () destroy_addr %pbs1 : $*@callee_guaranteed() -> () dealloc_stack %bs1 : $*@block_storage @callee_guaranteed () -> () destroy_value %sentinel2 : $@callee_guaranteed () -> () br bb3(%copyblock1 : $@convention(block) @noescape () -> ()) bb2: %bs2 = alloc_stack $@block_storage @callee_guaranteed () -> () %pbs2 = project_block_storage %bs2 : $*@block_storage @callee_guaranteed () -> () store %sentinel3 to [init] %pbs2 : $*@callee_guaranteed () -> () %initblock2 = init_block_storage_header %bs2 : $*@block_storage @callee_guaranteed () -> (), invoke %calleethunk : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> (), type $@convention(block) @noescape () -> () %copyblock2 = copy_block %initblock2 : $@convention(block) @noescape () -> () destroy_addr %pbs2 : $*@callee_guaranteed() -> () dealloc_stack %bs2 : $*@block_storage @callee_guaranteed () -> () destroy_value %sentinel2 : $@callee_guaranteed () -> () br bb3(%copyblock2 : $@convention(block) @noescape () -> ()) bb3(%block : @owned $@convention(block) @noescape () -> ()): %access = begin_access [modify] [unknown] %boxadr : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %f = function_ref @takesInoutAndNoEscapeBlockClosure : $@convention(thin) (@inout Int, @owned @convention(block) @noescape () -> ()) -> () %call = apply %f(%access, %block) : $@convention(thin) (@inout Int, @owned @convention(block) @noescape () -> ()) -> () end_access %access : $*Int destroy_value %pa : $@callee_guaranteed () -> () destroy_value %box : ${ var Int } %ret = tuple () return %ret : $() } // ----------------------------------------------------------------------------- // [SR-8266]: Compiler crash when checking // exclusivity of inout alias: // closureWithNoCapture // closureWithConflict, // partialApplyPhiThunk // testPartialApplyPhi. // testDirectPartialApplyPhi. // // Test that 'checkNoEscapePartialApply' does not assert on a noescape // closure passed through a block argument. sil hidden [ossa] @closureWithNoCapture : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %2 = tuple () return %2 : $() } sil hidden [ossa] @closureWithConflict : $@convention(thin) (Int, @inout_aliasable Int) -> () { bb0(%0 : $Int, %1 : $*Int): %2 = begin_access [modify] [unknown] %1 : $*Int // expected-note {{conflicting access is here}} end_access %2 : $*Int %v = tuple () return %v : $() } sil shared [transparent] [serializable] [reabstraction_thunk] [ossa] @partialApplyPhiThunk : $@convention(thin) (@in_guaranteed Int, @guaranteed @noescape @callee_guaranteed (Int) -> (@error Error)) -> (@error Error) { bb0(%0 : $*Int, %1 : $@noescape @callee_guaranteed (Int) -> (@error Error)): %val = load [trivial] %0 : $*Int try_apply %1(%val) : $@noescape @callee_guaranteed (Int) -> (@error Error), normal bb1, error bb2 bb1(%v : $()): return %v : $() bb2(%5 : @owned $Error): throw %5 : $Error } sil [ossa] @takeGenericNoEscapeFunction : $@convention(method) <τ_0_0> (@inout τ_0_0, @noescape @callee_guaranteed (@in_guaranteed τ_0_0) -> (@error Error)) -> (@error Error) // CHECK-LABEL: sil [ossa] @testPartialApplyPhi sil [ossa] @testPartialApplyPhi : $@convention(thin) (Int, @inout Int) -> (@error Error) { bb0(%0 : $Int, %1 : $*Int): cond_br undef, bb1, bb2 bb1: %f1 = function_ref @closureWithNoCapture : $@convention(thin) (Int) -> () %pa1 = partial_apply [callee_guaranteed] %f1() : $@convention(thin) (Int) -> () br bb3(%pa1 : $@callee_guaranteed (Int) -> ()) bb2: %f2 = function_ref @closureWithConflict : $@convention(thin) (Int, @inout_aliasable Int) -> () %pa2 = partial_apply [callee_guaranteed] %f2(%1) : $@convention(thin) (Int, @inout_aliasable Int) -> () br bb3(%pa2 : $@callee_guaranteed (Int) -> ()) bb3(%pa3 : @owned $@callee_guaranteed (Int) -> ()): %cvt3 = convert_function %pa3 : $@callee_guaranteed (Int) -> () to $@callee_guaranteed (Int) -> (@error Error) %esc3 = convert_escape_to_noescape [not_guaranteed] %cvt3 : $@callee_guaranteed (Int) -> (@error Error) to $@noescape @callee_guaranteed (Int) -> (@error Error) %f3 = function_ref @partialApplyPhiThunk : $@convention(thin) (@in_guaranteed Int, @guaranteed @noescape @callee_guaranteed (Int) -> (@error Error)) -> (@error Error) %pa4 = partial_apply [callee_guaranteed] %f3(%esc3) : $@convention(thin) (@in_guaranteed Int, @guaranteed @noescape @callee_guaranteed (Int) -> (@error Error)) -> (@error Error) %esc4 = convert_escape_to_noescape [not_guaranteed] %pa4 : $@callee_guaranteed (@in_guaranteed Int) -> (@error Error) to $@noescape @callee_guaranteed (@in_guaranteed Int) -> (@error Error) // ClosureLifetimeFixup has not run yet, so these destroys are "incorrect". destroy_value %pa4 : $@callee_guaranteed (@in_guaranteed Int) -> (@error Error) destroy_value %cvt3 : $@callee_guaranteed (Int) -> (@error Error) %access = begin_access [modify] [static] %1 : $*Int // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %f4 = function_ref @takeGenericNoEscapeFunction : $@convention(method) <τ_0_0> (@inout τ_0_0, @noescape @callee_guaranteed (@in_guaranteed τ_0_0) -> (@error Error)) -> (@error Error) try_apply %f4(%access, %esc4) : $@convention(method) <τ_0_0> (@inout τ_0_0, @noescape @callee_guaranteed (@in_guaranteed τ_0_0) -> (@error Error)) -> (@error Error), normal bb4, error bb5 bb4(%v : $()): end_access %access : $*Int return %v : $() bb5(%e : @owned $Error): end_access %access : $*Int throw %e : $Error } // CHECK-LABEL: sil [ossa] @testDirectPartialApplyPhi sil [ossa] @testDirectPartialApplyPhi : $@convention(thin) (@inout Int) -> (@error Error) { bb0(%0 : $*Int): cond_br undef, bb1, bb2 bb1: %f1 = function_ref @closureForDirectPartialApplyTakingInout : $@convention(thin) (@inout Int, @inout_aliasable Int) -> () %pa1 = partial_apply [callee_guaranteed] %f1(%0) : $@convention(thin) (@inout Int, @inout_aliasable Int) -> () br bb3(%pa1 : $@callee_guaranteed (@inout Int) -> ()) bb2: %f2 = function_ref @closureForDirectPartialApplyTakingInout : $@convention(thin) (@inout Int, @inout_aliasable Int) -> () %pa2 = partial_apply [callee_guaranteed] %f2(%0) : $@convention(thin) (@inout Int, @inout_aliasable Int) -> () br bb3(%pa2 : $@callee_guaranteed (@inout Int) -> ()) bb3(%pa3 : @owned $@callee_guaranteed (@inout Int) -> ()): %esc3 = convert_escape_to_noescape [not_guaranteed] %pa3 : $@callee_guaranteed (@inout Int) -> () to $@noescape @callee_guaranteed (@inout Int) -> () destroy_value %pa3 : $@callee_guaranteed (@inout Int) -> () %access = begin_access [modify] [static] %0 : $*Int // expected-error 2 {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %call = apply %esc3(%access) : $@noescape @callee_guaranteed (@inout Int) -> () end_access %access : $*Int %v = tuple () return %v : $() } // ----------------------------------------------------------------------------- // Test withoutActuallyEscaping thunk. // Assertion in DiagnoseStaticExclusivity // Noescape closure verification should not assert. sil @takeEscapingClosure : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () sil private [ossa] @nestedInWithoutActuallyEscapingVerify : $@convention(thin) (@inout_aliasable Int) -> () { bb0(%0 : $*Int): %a = begin_access [modify] [unknown] %0 : $*Int end_access %a : $*Int %v = tuple () return %v : $() } // CHECK-LABEL: sil hidden [ossa] @testWithoutActuallyEscapingVerify : $@convention(thin) (@inout Int) -> () { sil hidden [ossa] @testWithoutActuallyEscapingVerify : $@convention(thin) (@inout Int) -> () { bb0(%0 : $*Int): %2 = function_ref @nestedInWithoutActuallyEscapingVerify : $@convention(thin) (@inout_aliasable Int) -> () %3 = partial_apply [callee_guaranteed] %2(%0) : $@convention(thin) (@inout_aliasable Int) -> () %4 = function_ref @thunkForWithoutActuallyEscapingVerify : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () %3a = copy_value %3 : $@callee_guaranteed () -> () %5 = partial_apply [callee_guaranteed] %4(%3a) : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () %6 = mark_dependence %5 : $@callee_guaranteed () -> () on %3 : $@callee_guaranteed () -> () %8 = function_ref @closureForWithoutActuallyEscapingVerify : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () %9 = apply %8(%6) : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () %10 = is_escaping_closure %6 : $@callee_guaranteed () -> () cond_fail %10 : $Builtin.Int1 destroy_value %3 : $@callee_guaranteed () -> () destroy_value %6 : $@callee_guaranteed () -> () %14 = tuple () return %14 : $() } // CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] [without_actually_escaping] [ossa] @thunkForWithoutActuallyEscapingVerify : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () { sil shared [transparent] [serializable] [reabstraction_thunk] [without_actually_escaping] [ossa] @thunkForWithoutActuallyEscapingVerify : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () { bb0(%0 : @guaranteed $@callee_guaranteed () -> ()): %1 = apply %0() : $@callee_guaranteed () -> () return %1 : $() } sil private [ossa] @closureForWithoutActuallyEscapingVerify : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () { bb0(%0 : @guaranteed $@callee_guaranteed () -> ()): %2 = function_ref @takeEscapingClosure : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () %3 = apply %2(%0) : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () %4 = tuple () return %4 : $() } // ----------------------------------------------------------------------------- // Test withoutActuallyEscaping convert_function. // Assertion in DiagnoseStaticExclusivity // Noescape closure verification should not assert. // CHECK-LABEL: sil hidden [ossa] @testWithoutActuallyEscapingBlockVerify : $@convention(thin) (Int) -> () { // CHECK: convert_function %{{.*}} : $@convention(block) () -> () to [without_actually_escaping] $@convention(block) () -> () sil hidden [ossa] @testWithoutActuallyEscapingBlockVerify : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %box = alloc_box ${ var Int }, var, name "localVal" %projbox = project_box %box : ${ var Int }, 0 store %0 to [trivial] %projbox : $*Int %closureF = function_ref @testWithoutActuallyEscapingBlockVerifyClosure : $@convention(thin) (@inout_aliasable Int) -> () %closure = partial_apply [callee_guaranteed] %closureF(%projbox) : $@convention(thin) (@inout_aliasable Int) -> () %block = alloc_stack $@block_storage @callee_guaranteed () -> () %blockproj = project_block_storage %block : $*@block_storage @callee_guaranteed () -> () store %closure to [init] %blockproj : $*@callee_guaranteed () -> () // function_ref thunk for @escaping @callee_guaranteed () -> () %thunkF = function_ref @$sIeg_IeyB_TR : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> () %initblock_unowned = init_block_storage_header %block : $*@block_storage @callee_guaranteed () -> (), invoke %thunkF : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> (), type $@convention(block) () -> () %initblock = copy_block %initblock_unowned : $@convention(block) () -> () destroy_addr %blockproj : $*@callee_guaranteed () -> () dealloc_stack %block : $*@block_storage @callee_guaranteed () -> () %borrow = begin_borrow %initblock : $@convention(block) () -> () %copy = copy_value %borrow : $@convention(block) () -> () %escapingF = convert_function %copy : $@convention(block) () -> () to [without_actually_escaping] $@convention(block) () -> () %clauseF = function_ref @testWithoutActuallyEscapingBlockVerifyClause : $@convention(thin) (@guaranteed @convention(block) () -> ()) -> () %call = apply %clauseF(%escapingF) : $@convention(thin) (@guaranteed @convention(block) () -> ()) -> () destroy_value %escapingF : $@convention(block) () -> () end_borrow %borrow : $@convention(block) () -> () destroy_value %initblock : $@convention(block) () -> () destroy_value %box : ${ var Int } %v = tuple () return %v : $() } sil [ossa] @testWithoutActuallyEscapingBlockVerifyClosure : $@convention(thin) (@inout_aliasable Int) -> () // thunk for @escaping @callee_guaranteed () -> () sil shared [transparent] [serializable] [reabstraction_thunk] [ossa] @$sIeg_IeyB_TR : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> () { // %0 bb0(%0 : $*@block_storage @callee_guaranteed () -> ()): %1 = project_block_storage %0 : $*@block_storage @callee_guaranteed () -> () %2 = load [copy] %1 : $*@callee_guaranteed () -> () %3 = begin_borrow %2 : $@callee_guaranteed () -> () %4 = apply %3() : $@callee_guaranteed () -> () end_borrow %3 : $@callee_guaranteed () -> () %6 = tuple () destroy_value %2 : $@callee_guaranteed () -> () return %6 : $() } // end sil function '$sIeg_IeyB_TR' sil private [ossa] @testWithoutActuallyEscapingBlockVerifyClause : $@convention(thin) (@guaranteed @convention(block) () -> ()) -> () { bb0(%0 : @guaranteed $@convention(block) () -> ()): %1 = copy_block %0 : $@convention(block) () -> () %3 = begin_borrow %1 : $@convention(block) () -> () %4 = copy_value %3 : $@convention(block) () -> () %5 = apply %4() : $@convention(block) () -> () destroy_value %4 : $@convention(block) () -> () end_borrow %3 : $@convention(block) () -> () destroy_value %1 : $@convention(block) () -> () %v = tuple () return %v : $() } //----------------------------------------------------------------------------- // Test dynamically replaceable. Diagnostics should not be able to // make assumptions about the accesses in the callee function. struct TestDynamic { @_hasStorage @_hasInitialValue var x: Builtin.Int64 { get set } @_hasStorage @_hasInitialValue var y: Builtin.Int64 { get set } public mutating func foo() init(x: Builtin.Int64, y: Builtin.Int64) init() } @_hasStorage @_hasInitialValue var s: S { get set } sil hidden [ossa] @testCallDynamic : $@convention(method) (@inout TestDynamic) -> () { bb0(%0 : $*TestDynamic): %access = begin_access [modify] [unknown] %0 : $*TestDynamic // expected-error {{overlapping accesses, but modification requires exclusive access; consider copying to a local variable}} %sea = struct_element_addr %access : $*TestDynamic, #TestDynamic.x %f = dynamic_function_ref @testDynamicLocal : $@convention(thin) (@inout Builtin.Int64, @inout_aliasable TestDynamic) -> () %call = apply %f(%sea, %0) : $@convention(thin) (@inout Builtin.Int64, @inout_aliasable TestDynamic) -> () // expected-note {{conflicting access is here}} end_access %access : $*TestDynamic %v = tuple () return %v : $() } // This callee actually accesses different subpaths, but the caller's // diagnostic should not be able to see that. sil private [dynamically_replacable] [ossa] @testDynamicLocal : $@convention(thin) (@inout Builtin.Int64, @inout_aliasable TestDynamic) -> () { bb0(%0 : $*Builtin.Int64, %1 : $*TestDynamic): %literal = integer_literal $Builtin.Int64, 1 %access1 = begin_access [modify] [unknown] %0 : $*Builtin.Int64 assign %literal to %access1 : $*Builtin.Int64 end_access %access1 : $*Builtin.Int64 %access2 = begin_access [modify] [unknown] %1 : $*TestDynamic %sea = struct_element_addr %access2 : $*TestDynamic, #TestDynamic.y assign %literal to %sea : $*Builtin.Int64 end_access %access2 : $*TestDynamic %19 = tuple () return %19 : $() }