| // 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) <T> (@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 @twoLocalInoutsDisaliased |
| sil hidden @twoLocalInoutsDisaliased : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @twoLocalInoutsSimpleAliasing |
| sil hidden @twoLocalInoutsSimpleAliasing : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @conflictingPriorAccess |
| sil hidden @conflictingPriorAccess : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @twoSequentialInouts |
| sil hidden @twoSequentialInouts : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @unconditionalBranch |
| sil hidden @unconditionalBranch : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @diamondMergeStacks |
| sil hidden @diamondMergeStacks : $@convention(thin) (Int, Builtin.Int1) -> () { |
| bb0(%0 : @trivial $Int, %1 : @trivial $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 @loopMergeStacks |
| sil hidden @loopMergeStacks : $@convention(thin) (Int, Builtin.Int1) -> () { |
| bb0(%0 : @trivial $Int, %1 : @trivial $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 @loopWithError |
| sil hidden @loopWithError : $@convention(thin) (Int, Builtin.Int1) -> () { |
| bb0(%0 : @trivial $Int, %1 : @trivial $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 @modifySubAccessesAreAllowed |
| sil hidden @modifySubAccessesAreAllowed : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @twoLocalReadsSimpleAliasing |
| sil hidden @twoLocalReadsSimpleAliasing : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @localReadFollowedByModify |
| sil hidden @localReadFollowedByModify : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @localModifyFollowedByRead |
| sil hidden @localModifyFollowedByRead : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 { |
| @sil_stored var f: Int |
| init() |
| } |
| // CHECK-LABEL: sil hidden @classStoredProperty |
| sil hidden @classStoredProperty : $@convention(thin) (ClassWithStoredProperty) -> () { |
| bb0(%0 : @unowned $ClassWithStoredProperty): |
| %1 = ref_element_addr %0 : $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 %0 : $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 |
| destroy_value %0 : $ClassWithStoredProperty |
| %5 = tuple () |
| return %5 : $() |
| } |
| |
| // CHECK-LABEL: sil hidden @lookThroughBeginBorrow |
| sil hidden @lookThroughBeginBorrow : $@convention(thin) (ClassWithStoredProperty) -> () { |
| bb0(%0 : @unowned $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 @twoAllocBoxProjections |
| sil hidden @twoAllocBoxProjections : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @lookThroughMarkUninitialized |
| sil hidden @lookThroughMarkUninitialized : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @modifySameGlobal |
| sil hidden @modifySameGlobal : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @modifyDifferentGlobal |
| sil hidden @modifyDifferentGlobal : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @readWriteReadConflictingThirdAccess |
| sil hidden @readWriteReadConflictingThirdAccess : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @writeWriteWriteConflictingThirdAccess |
| sil hidden @writeWriteWriteConflictingThirdAccess : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @resetFirstAccessForNote |
| sil hidden @resetFirstAccessForNote : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @blockMapRehash : $@convention(method) (Builtin.Int1) -> () { |
| bb0(%0 : @trivial $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 @pointerToAddr |
| sil hidden @pointerToAddr : $@convention(thin) (Builtin.RawPointer) -> Int { |
| bb0(%0: @trivial $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 @inlinedStructElement |
| sil hidden @inlinedStructElement : $@convention(thin) (@inout S) -> Int { |
| bb0(%0 : @trivial $*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 @inlinedTupleElement |
| sil hidden @inlinedTupleElement : $@convention(thin) (@inout (Int, Int)) -> Int { |
| bb0(%0 : @trivial $*(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 @inlinedEnumValue |
| sil hidden @inlinedEnumValue : $@convention(thin) (Int) -> (@out Optional<Int>, Int) { |
| bb0(%0 : @trivial $*Optional<Int>, %1 : @trivial $Int): |
| %6 = unchecked_take_enum_data_addr %0 : $*Optional<Int>, #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 @inlinedArrayProp |
| sil hidden @inlinedArrayProp : $@convention(thin) (@guaranteed Storage, Builtin.Word) -> Int { |
| bb0(%0 : @guaranteed $Storage, %1 : @trivial $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 @closureThatModifiesCaptureAndTakesInout: $@convention(thin) (@inout Int, @inout_aliasable Int) -> () { |
| bb0(%0 : @trivial $*Int, %1 : @trivial $*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 @inProgressModifyModifyConflictWithCallToClosure |
| sil hidden @inProgressModifyModifyConflictWithCallToClosure : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @closureWithArgument_1 : $@convention(thin) (Int, @inout_aliasable Int) -> () { |
| bb0(%0 : @trivial $Int, %1 : @trivial $*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 @inProgressConflictWithNoEscapeClosureArgument |
| sil hidden @inProgressConflictWithNoEscapeClosureArgument : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 %2 : ${ var Int } |
| %9 = tuple () |
| return %9 : $() |
| } |
| |
| // CHECK-LABEL: sil hidden @inProgressConflictWithNoEscapeGuaranteedClosureArgument : $@convention(thin) (Int) -> () { |
| sil hidden @inProgressConflictWithNoEscapeGuaranteedClosureArgument : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 %2 : ${ var Int } |
| %9 = tuple () |
| return %9 : $() |
| } |
| |
| sil hidden @closureThatModifiesCapture_2 : $@convention(thin) (@inout_aliasable Int) -> () { |
| bb0(%0 : @trivial $*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 @inProgressReadModifyConflictWithNoEscapeClosureArgument |
| sil hidden @inProgressReadModifyConflictWithNoEscapeClosureArgument : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 %2 : ${ var Int } |
| %9 = tuple () |
| return %9 : $() |
| } |
| |
| sil hidden @closureWithConcreteReturn : $@convention(thin) (Int, @inout_aliasable Int) -> (Int) { |
| bb0(%0 : @trivial $Int, %1 : @trivial $*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 @inProgressConflictWithNoEscapeClosureWithReabstractionThunk |
| sil hidden @inProgressConflictWithNoEscapeClosureWithReabstractionThunk : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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) <T_0> (@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<Int>(%11, %10) : $@convention(thin) <T_0> (@inout Int, @noescape @callee_owned (Int) -> @out T_0) -> () |
| end_access %11: $*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 @closureThatModifiesCapture_1: $@convention(thin) (@inout_aliasable Int) -> () { |
| bb0(%0 : @trivial $*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 @inProgressConflictWithNoEscapeClosureWithBlockStorage |
| sil hidden @inProgressConflictWithNoEscapeClosureWithBlockStorage : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 %2 : ${ var Int } |
| %ret = tuple () |
| return %ret : $() |
| } |
| |
| // CHECK-LABEL: sil hidden @inProgressConflictWithNoEscapeClosureWithOptionalBlockStorage |
| sil hidden @inProgressConflictWithNoEscapeClosureWithOptionalBlockStorage : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 %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 @takesInoutIntAndStructWithStoredProps : $@convention(thin) (@inout Int, @inout StructWithStoredProps) -> () |
| |
| // CHECK-LABEL: sil hidden @twoSeparateStoredProperties |
| sil hidden @twoSeparateStoredProperties : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @twoSeparateNestedStoredProperties |
| sil hidden @twoSeparateNestedStoredProperties : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @theSameStoredProperty |
| sil hidden @theSameStoredProperty : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @storedPropertyAndAggregate |
| sil hidden @storedPropertyAndAggregate : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @nestedStoredPropertyAndAggregate |
| sil hidden @nestedStoredPropertyAndAggregate : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @twoSeparateTupleElements |
| sil hidden @twoSeparateTupleElements : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @sameTupleElement |
| sil hidden @sameTupleElement : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 @passNilToNoEscape : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 () -> ()>) -> () |
| %7 = tuple () |
| return %7 : $() |
| } |
| |
| sil private @closureForDirectPartialApplyTakingInout : $@convention(thin) (@inout Int, @inout_aliasable Int) -> () { |
| bb0(%0 : @trivial $*Int, %1 : @trivial $*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 @directPartialApplyTakingInout : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 %box : ${ var Int } |
| %v = tuple () |
| return %v : $() |
| } |
| |
| sil private @closureForDirectPartialApplyChain : $@convention(thin) (@inout Int, Int, @inout_aliasable Int) -> () { |
| bb0(%0 : @trivial $*Int, %1 : @trivial $Int, %2 : @trivial $*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 @directPartialApplyChain : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 %box : ${ var Int } |
| %v = tuple () |
| return %v : $() |
| } |
| |
| sil private @closureForPartialApplyArgChain : $@convention(thin) (Int, @inout_aliasable Int) -> () { |
| bb0(%0 : @trivial $Int, %1 : @trivial $*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 @partialApplyArgChain : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 %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 @lldb_unsupported_markuninitialized_testcase : $@convention(thin) (UnsafeMutablePointer<Any>) -> () { |
| bb0(%0 : @trivial $UnsafeMutablePointer<Any>): |
| %2 = struct_extract %0 : $UnsafeMutablePointer<Any>, #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<Any>, #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 @addressor : $@convention(thin) () -> UnsafeMutablePointer<Int32> |
| |
| // 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 @addressorAccess : $@convention(thin) (@guaranteed ClassWithStoredProperty, Int32) -> () { |
| bb0(%0 : @guaranteed $ClassWithStoredProperty, %1 : @trivial $Int32): |
| %f = function_ref @addressor : $@convention(thin) () -> UnsafeMutablePointer<Int32> |
| %up = apply %f() : $@convention(thin) () -> UnsafeMutablePointer<Int32> |
| %o = unchecked_ref_cast %0 : $ClassWithStoredProperty to $Builtin.NativeObject |
| %ptr = struct_extract %up : $UnsafeMutablePointer<Int32>, #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 : $() |
| } |
| |
| // <rdar://problem/41976355> [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 @mutatingNoescapeHelper : $@convention(thin) (Optional<UnsafePointer<Int32>>, @inout_aliasable Int32) -> () { |
| bb0(%0 : @trivial $Optional<UnsafePointer<Int32>>, %1 : @trivial $*Int32): |
| %up = unchecked_enum_data %0 : $Optional<UnsafePointer<Int32>>, #Optional.some!enumelt.1 |
| %p = struct_extract %up : $UnsafePointer<Int32>, #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] @mutatingNoescapeThunk : $@convention(thin) (UnsafePointer<Int32>, @guaranteed @callee_guaranteed (Optional<UnsafePointer<Int32>>) -> ()) -> () { |
| bb0(%0 : @trivial $UnsafePointer<Int32>, %1 : @guaranteed $@callee_guaranteed (Optional<UnsafePointer<Int32>>) -> ()): |
| %e = enum $Optional<UnsafePointer<Int32>>, #Optional.some!enumelt.1, %0 : $UnsafePointer<Int32> |
| %c = apply %1(%e) : $@callee_guaranteed (Optional<UnsafePointer<Int32>>) -> () |
| %v = tuple () |
| return %v : $() |
| } |
| |
| sil @takeMutatingNoescapeClosure : $@convention(thin) <Ï„_0_0> (UnsafePointer<Ï„_0_0>, @noescape @callee_guaranteed (UnsafePointer<Int32>) -> ()) -> () |
| |
| // 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 @mutatingNoescapeWithThunk : $@convention(method) (UnsafePointer<Int32>, @inout Int32) -> () { |
| bb0(%0 : @trivial $UnsafePointer<Int32>, %1 : @trivial $*Int32): |
| %f1 = function_ref @mutatingNoescapeHelper : $@convention(thin) (Optional<UnsafePointer<Int32>>, @inout_aliasable Int32) -> () |
| %pa1 = partial_apply [callee_guaranteed] %f1(%1) : $@convention(thin) (Optional<UnsafePointer<Int32>>, @inout_aliasable Int32) -> () |
| %thunk = function_ref @mutatingNoescapeThunk : $@convention(thin) (UnsafePointer<Int32>, @guaranteed @callee_guaranteed (Optional<UnsafePointer<Int32>>) -> ()) -> () |
| %pa2 = partial_apply [callee_guaranteed] %thunk(%pa1) : $@convention(thin) (UnsafePointer<Int32>, @guaranteed @callee_guaranteed (Optional<UnsafePointer<Int32>>) -> ()) -> () |
| %closure = convert_escape_to_noescape [not_guaranteed] %pa2 : $@callee_guaranteed (UnsafePointer<Int32>) -> () to $@noescape @callee_guaranteed (UnsafePointer<Int32>) -> () |
| %f2 = function_ref @takeMutatingNoescapeClosure : $@convention(thin) <Ï„_0_0> (UnsafePointer<Ï„_0_0>, @noescape @callee_guaranteed (UnsafePointer<Int32>) -> ()) -> () |
| %call = apply %f2<Int32>(%0, %closure) : $@convention(thin) <Ï„_0_0> (UnsafePointer<Ï„_0_0>, @noescape @callee_guaranteed (UnsafePointer<Int32>) -> ()) -> () |
| %v = tuple () |
| return %v : $() |
| } |
| |
| sil @takeInoutAndMutatingNoescapeClosure : $@convention(thin) <Ï„_0_0> (@inout Int32, UnsafePointer<Ï„_0_0>, @noescape @callee_guaranteed (UnsafePointer<Int32>) -> ()) -> () |
| |
| sil @mutatingNoescapeConflictWithThunk : $@convention(method) (UnsafePointer<Int32>, @inout Int32) -> () { |
| bb0(%0 : @trivial $UnsafePointer<Int32>, %1 : @trivial $*Int32): |
| %f1 = function_ref @mutatingNoescapeHelper : $@convention(thin) (Optional<UnsafePointer<Int32>>, @inout_aliasable Int32) -> () |
| %pa1 = partial_apply [callee_guaranteed] %f1(%1) : $@convention(thin) (Optional<UnsafePointer<Int32>>, @inout_aliasable Int32) -> () |
| %thunk = function_ref @mutatingNoescapeThunk : $@convention(thin) (UnsafePointer<Int32>, @guaranteed @callee_guaranteed (Optional<UnsafePointer<Int32>>) -> ()) -> () |
| %pa2 = partial_apply [callee_guaranteed] %thunk(%pa1) : $@convention(thin) (UnsafePointer<Int32>, @guaranteed @callee_guaranteed (Optional<UnsafePointer<Int32>>) -> ()) -> () |
| %closure = convert_escape_to_noescape [not_guaranteed] %pa2 : $@callee_guaranteed (UnsafePointer<Int32>) -> () to $@noescape @callee_guaranteed (UnsafePointer<Int32>) -> () |
| %f2 = function_ref @takeInoutAndMutatingNoescapeClosure : $@convention(thin) <Ï„_0_0> (@inout Int32, UnsafePointer<Ï„_0_0>, @noescape @callee_guaranteed (UnsafePointer<Int32>) -> ()) -> () |
| %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<Int32>(%access, %0, %closure) : $@convention(thin) <Ï„_0_0> (@inout Int32, UnsafePointer<Ï„_0_0>, @noescape @callee_guaranteed (UnsafePointer<Int32>) -> ()) -> () |
| end_access %access : $*Int32 |
| %v = tuple () |
| return %v : $() |
| } |
| |
| // <rdar://problem/41660554> 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 @testNestedKeypathAccess : $@convention(thin) (@guaranteed (UnsafeMutablePointer<Int>, Optional<AnyObject>)) -> () { |
| bb0(%0 : @guaranteed $(UnsafeMutablePointer<Int>, Optional<AnyObject>)): |
| %up = tuple_extract %0 : $(UnsafeMutablePointer<Int>, Optional<AnyObject>), 0 |
| %o = tuple_extract %0 : $(UnsafeMutablePointer<Int>, Optional<AnyObject>), 1 |
| %p = struct_extract %up : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue |
| %adr = pointer_to_address %p : $Builtin.RawPointer to [strict] $*Int |
| %dep = mark_dependence %adr : $*Int on %o : $Optional<AnyObject> |
| %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 @noEscapeClosureWithConditionalBlockStorage : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 () -> () |
| 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 () -> () |
| 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 %box : ${ var Int } |
| %ret = tuple () |
| return %ret : $() |
| } |
| |
| // ----------------------------------------------------------------------------- |
| // <rdar://problem/42242406> [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 @closureWithNoCapture : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $Int): |
| %2 = tuple () |
| return %2 : $() |
| } |
| |
| sil hidden @closureWithConflict : $@convention(thin) (Int, @inout_aliasable Int) -> () { |
| bb0(%0 : @trivial $Int, %1 : @trivial $*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] @partialApplyPhiThunk : $@convention(thin) (@in_guaranteed Int, @guaranteed @noescape @callee_guaranteed (Int) -> (@error Error)) -> (@error Error) { |
| bb0(%0 : @trivial $*Int, %1 : @trivial $@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 : @trivial $()): |
| return %v : $() |
| |
| bb2(%5 : @owned $Error): |
| throw %5 : $Error |
| } |
| |
| sil @takeGenericNoEscapeFunction : $@convention(method) <Ï„_0_0> (@inout Ï„_0_0, @noescape @callee_guaranteed (@in_guaranteed Ï„_0_0) -> (@error Error)) -> (@error Error) |
| |
| // CHECK-LABEL: sil @testPartialApplyPhi |
| sil @testPartialApplyPhi : $@convention(thin) (Int, @inout Int) -> (@error Error) { |
| bb0(%0 : @trivial $Int, %1 : @trivial $*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<Int>(%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 : @trivial $()): |
| end_access %access : $*Int |
| return %v : $() |
| |
| bb5(%e : @owned $Error): |
| end_access %access : $*Int |
| throw %e : $Error |
| } |
| |
| // CHECK-LABEL: sil @testDirectPartialApplyPhi |
| sil @testDirectPartialApplyPhi : $@convention(thin) (@inout Int) -> (@error Error) { |
| bb0(%0 : @trivial $*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. |
| // <rdar://problem/43059088> Assertion in DiagnoseStaticExclusivity |
| // Noescape closure verification should not assert. |
| |
| sil @takeEscapingClosure : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () |
| |
| sil private @nestedInWithoutActuallyEscapingVerify : $@convention(thin) (@inout_aliasable Int) -> () { |
| bb0(%0 : @trivial $*Int): |
| %a = begin_access [modify] [unknown] %0 : $*Int |
| end_access %a : $*Int |
| %v = tuple () |
| return %v : $() |
| } |
| |
| // CHECK-LABEL: sil hidden @testWithoutActuallyEscapingVerify : $@convention(thin) (@inout Int) -> () { |
| sil hidden @testWithoutActuallyEscapingVerify : $@convention(thin) (@inout Int) -> () { |
| bb0(%0 : @trivial $*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 () -> ()) -> () |
| %5 = partial_apply [callee_guaranteed] %4(%3) : $@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 %6 : $@callee_guaranteed () -> () |
| %14 = tuple () |
| return %14 : $() |
| } |
| |
| // CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] [without_actually_escaping] @thunkForWithoutActuallyEscapingVerify : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () { |
| sil shared [transparent] [serializable] [reabstraction_thunk] [without_actually_escaping] @thunkForWithoutActuallyEscapingVerify : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () { |
| bb0(%0 : @guaranteed $@callee_guaranteed () -> ()): |
| %1 = apply %0() : $@callee_guaranteed () -> () |
| return %1 : $() |
| } |
| |
| sil private @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. |
| // <rdar://problem/43059088> Assertion in DiagnoseStaticExclusivity |
| // Noescape closure verification should not assert. |
| |
| // CHECK-LABEL: sil hidden @testWithoutActuallyEscapingBlockVerify : $@convention(thin) (Int) -> () { |
| // CHECK: convert_function %{{.*}} : $@convention(block) () -> () to [without_actually_escaping] $@convention(block) () -> () |
| sil hidden @testWithoutActuallyEscapingBlockVerify : $@convention(thin) (Int) -> () { |
| bb0(%0 : @trivial $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 = init_block_storage_header %block : $*@block_storage @callee_guaranteed () -> (), invoke %thunkF : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> (), type $@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 @testWithoutActuallyEscapingBlockVerifyClosure : $@convention(thin) (@inout_aliasable Int) -> () |
| |
| // thunk for @escaping @callee_guaranteed () -> () |
| sil shared [transparent] [serializable] [reabstraction_thunk] @$sIeg_IeyB_TR : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> () { |
| // %0 |
| bb0(%0 : @trivial $*@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 @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 : $() |
| } |