Files
swift-mirror/test/SILGen/discard.swift
Erik Eckstein 7cceaff5f3 SIL: don't print operand types in textual SIL
Type annotations for instruction operands are omitted, e.g.

```
  %3 = struct $S(%1, %2)
```

Operand types are redundant anyway and were only used for sanity checking in the SIL parser.

But: operand types _are_ printed if the definition of the operand value was not printed yet.
This happens:

* if the block with the definition appears after the block where the operand's instruction is located

* if a block or instruction is printed in isolation, e.g. in a debugger

The old behavior can be restored with `-Xllvm -sil-print-types`.
This option is added to many existing test files which check for operand types in their check-lines.
2024-11-21 18:49:52 +01:00

199 lines
7.7 KiB
Swift

// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types -enable-experimental-feature MoveOnlyEnumDeinits -module-name test %s | %FileCheck %s --enable-var-scope
// RUN: %target-swift-emit-sil -Xllvm -sil-print-types -enable-experimental-feature MoveOnlyEnumDeinits -module-name test -sil-verify-all %s | %FileCheck %s --check-prefix CHECK-SIL --enable-var-scope
// Swift sources are require to remove struct_extract so this check-not line passes:
// "CHECK-SIL-NOT: struct_extract"
// REQUIRES: swift_in_compiler
// REQUIRES: swift_feature_MoveOnlyEnumDeinits
func invokedDeinit() {}
enum MaybeFile: ~Copyable {
case some(File)
case none
deinit {}
// NOTE: we can't pattern match on self since
// that would consume it before we can discard self!
var test: Int {
__consuming get {
discard self
return 0
}
}
// CHECK-LABEL: sil hidden [ossa] @$s4test9MaybeFileOAASivg
// CHECK: [[SELF_BOX:%.*]] = alloc_box ${ let MaybeFile }, let, name "self", argno 1
// CHECK: [[SELF_REF:%.*]] = project_box [[SELF_BOX]] : ${ let MaybeFile }, 0
// CHECK: store {{.*}} to [init]
// CHECK: [[SELF_MMC:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[SELF_REF]] : $*MaybeFile
// CHECK: [[SELF_VAL:%.*]] = load [copy] [[SELF_MMC]] : $*MaybeFile
// CHECK: [[DD:%.*]] = drop_deinit [[SELF_VAL]] : $MaybeFile
// CHECK: destroy_value [[DD]] : $MaybeFile
// CHECK-SIL-LABEL: sil hidden @$s4test9MaybeFileOAASivg
// CHECK-SIL: [[SELF_STACK:%.*]] = alloc_stack $MaybeFile, let, name "self", argno 1
// CHECK-SIL: store {{.*}}
// CHECK-SIL: [[SELF_VAL:%.*]] = load [[SELF_STACK]] : $*MaybeFile
// CHECK-SIL: switch_enum [[SELF_VAL]] : $MaybeFile, case #MaybeFile.some!enumelt: bb1, case #MaybeFile.none!enumelt: bb2
//
// CHECK-SIL: bb1([[FILE:%.*]] : $File):
// CHECK-SIL: release_value [[FILE]] : $File
}
struct File: ~Copyable {
let fd: Int
static var nextFD: Int = 0
init() {
fd = File.nextFD
File.nextFD += 1
}
__consuming func takeDescriptor() -> Int {
let id = fd
discard self
return id
}
// CHECK-LABEL: sil hidden [ossa] @$s4test4FileV14takeDescriptorSiyF
// CHECK: [[SELF_BOX:%.*]] = alloc_box ${ let File }, let, name "self", argno 1
// CHECK: [[SELF_REF:%.*]] = project_box [[SELF_BOX]] : ${ let File }, 0
// CHECK: store {{.*}} to [init]
// CHECK: load_borrow {{.*}} : $*File
// CHECK: [[SELF_MMC:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[SELF_REF]] : $*File
// CHECK: [[SELF_VAL:%.*]] = load [copy] [[SELF_MMC]] : $*File
// CHECK: [[DD:%.*]] = drop_deinit [[SELF_VAL]] : $File
// CHECK: destroy_value [[DD]] : $File
deinit {
invokedDeinit()
}
}
struct PointerTree: ~Copyable {
let left: UnsafePointer<UInt>?
let file: Int = 0
lazy var popularity: Int = 0
var right: Float = 0.0
consuming func tryDestroy(doDiscard: Bool) throws {
if doDiscard {
discard self
} else {
_ = consume self
}
throw E.err
}
// CHECK-LABEL: sil hidden [ossa] @$s4test11PointerTreeV10tryDestroy9doDiscardySb_tKF : $@convention(method) (Bool, @owned PointerTree) -> @error any Error {
// CHECK: bb0{{.*}}:
// CHECK: [[SELF_BOX:%.*]] = alloc_box ${ var PointerTree }, var, name "self"
// CHECK: [[SELF_BOX_LIFETIME:%.*]] = begin_borrow [lexical] [var_decl] [[SELF_BOX]]
// CHECK: [[SELF_PTR:%.*]] = project_box [[SELF_BOX_LIFETIME]] : ${ var PointerTree }, 0
// .. skip to the conditional test ..
// CHECK: [[SHOULD_FORGET:%.*]] = struct_extract {{.*}} : $Bool, #Bool._value
// CHECK: cond_br [[SHOULD_FORGET]], bb1, bb2
//
// CHECK: bb1:
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[SELF_PTR]] : $*PointerTree
// CHECK: [[MMC:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[ACCESS]] : $*PointerTree
// CHECK: [[COPIED_SELF:%.*]] = load [copy] [[MMC]] : $*PointerTree
// CHECK: end_access [[ACCESS]] : $*PointerTree
// CHECK: [[DD:%.*]] = drop_deinit [[COPIED_SELF]]
// CHECK: destroy_value [[DD]]
// CHECK: br bb3
//
// CHECK: bb2:
// CHECK: br bb3
//
// CHECK: bb3:
// CHECK: end_borrow [[SELF_BOX_LIFETIME]]
// CHECK: destroy_value [[SELF_BOX]] : ${ var PointerTree }
// CHECK: throw
// CHECK: } // end sil function
// After the mandatory passes have run, check for correct deinitializations within the init.
// CHECK-SIL-LABEL: sil hidden @$s4test11PointerTreeV10tryDestroy9doDiscardySb_tKF
// CHECK-SIL: [[SHOULD_FORGET:%.*]] = struct_extract {{.*}} : $Bool, #Bool._value
// CHECK-SIL: cond_br [[SHOULD_FORGET]], bb1, bb2
//
// CHECK-SIL: bb1:
// CHECK-SIL: [[ACCESS:%.*]] = begin_access [modify] [static] {{.*}} : $*PointerTree
// CHECK-SIL: [[SELF_VAL:%.*]] = load [[ACCESS]] : $*PointerTree
// CHECK-SIL: end_access [[ACCESS]] : $*PointerTree
// CHECK-SIL-NOT: struct_extract
// no accesses to the fields are expected because the fields are no-op destroyed.
// CHECK-SIL: br bb3
//
// CHECK-SIL: bb2:
// CHECK-SIL: destroy_addr %{{.*}} : $*PointerTree
// CHECK-SIL: br bb3
//
// CHECK-SIL: bb3:
// CHECK-SIL-NOT: apply
// CHECK-SIL: throw
deinit {
invokedDeinit()
}
}
final class Wallet {
var numCards = 0
}
enum Ticket: ~Copyable {
case empty
case within(Wallet)
consuming func changeTicket(inWallet wallet: Wallet? = nil) {
if let existingWallet = wallet {
self = .within(existingWallet)
_ = consume self
} else {
discard self
}
}
// CHECK-LABEL: sil hidden [ossa] @$s4test6TicketO06changeB08inWalletyAA0E0CSg_tF : $@convention(method) (@guaranteed Optional<Wallet>, @owned Ticket) -> () {
// CHECK: [[SELF_REF:%.*]] = project_box [[SELF_BOX:%.*]] : ${ var Ticket }, 0
// CHECK: switch_enum {{.*}} : $Optional<Wallet>, case #Optional.some!enumelt: {{.*}}, case #Optional.none!enumelt: [[NO_WALLET_BB:bb[0-9]+]]
//
// >> now we begin the destruction sequence, which involves pattern matching on self to destroy its innards
// CHECK: [[NO_WALLET_BB]]
// CHECK: [[SELF_ACCESS:%.*]] = begin_access [read] [unknown] {{%.*}} : $*Ticket
// CHECK: [[SELF_MMC:%.*]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[SELF_ACCESS]]
// CHECK: [[SELF_COPY:%.*]] = load [copy] [[SELF_MMC]] : $*Ticket
// CHECK: end_access [[SELF_ACCESS:%.*]] : $*Ticket
// CHECK: [[DD:%.*]] = drop_deinit [[SELF_COPY]] : $Ticket
// CHECK: destroy_value [[DD]] : $Ticket
// CHECK-SIL-LABEL: sil hidden @$s4test6TicketO06changeB08inWalletyAA0E0CSg_tF : $@convention(method) (@guaranteed Optional<Wallet>, @owned Ticket) -> () {
// CHECK-SIL: [[SELF_REF:%.*]] = alloc_stack [lexical] [var_decl] $Ticket, var, name "self"
// CHECK-SIL: switch_enum {{.*}} : $Optional<Wallet>, case #Optional.some!enumelt: {{.*}}, case #Optional.none!enumelt: [[NO_WALLET_BB:bb[0-9]+]]
//
// >> now we begin the destruction sequence, which involves pattern matching on self to destroy its innards
// CHECK-SIL: [[NO_WALLET_BB]]
// CHECK-SIL: [[SELF_ACCESS:%.*]] = begin_access [modify] [static] {{%.*}} : $*Ticket
// CHECK-SIL: [[SELF_COPY:%.*]] = load [[SELF_ACCESS]] : $*Ticket
// CHECK-SIL: end_access [[SELF_ACCESS:%.*]] : $*Ticket
// CHECK-SIL: switch_enum [[SELF_COPY]] : $Ticket, case #Ticket.empty!enumelt: [[TICKET_EMPTY:bb[0-9]+]], case #Ticket.within!enumelt: [[TICKET_WITHIN:bb[0-9]+]]
// CHECK-SIL: [[TICKET_EMPTY]]:
// CHECK-SIL: br [[JOIN_POINT:bb[0-9]+]]
// CHECK-SIL: [[TICKET_WITHIN]]([[PREV_SELF_WALLET:%.*]] : $Wallet):
// CHECK-SIL: strong_release [[PREV_SELF_WALLET]] : $Wallet
// CHECK-SIL: br [[JOIN_POINT]]
// CHECK-SIL: [[JOIN_POINT]]:
deinit {
print("destroying ticket")
}
}
enum E: Error { case err }
class Ptr { var whatever: Int = 0 }