Files
swift-mirror/test/SILGen/keypaths.swift
Yuta Saito c5314bd3af Centralize KeyPath accessor calling convention logic to IRGen
KeyPath's getter/setter/hash/equals functions have their own calling
convention, which receives generic arguments and embedded indices from a
given KeyPath argument buffer.
The convention was previously implemented by:
1. Accepting an argument buffer as an UnsafeRawPointer and casting it to
   indices tuple pointer in SIL.
2. Bind generic arguments info from the given argument buffer while emitting
   prologue in IRGen by creating a new forwarding thunk.

This 2-phase lowering approach was not ideal, as it blocked KeyPath
projection optimization [^1], and also required having a target arch
specific signature lowering logic in SIL-level [^2].

This patch centralizes the KeyPath accessor calling convention logic to
IRGen, by introducing `@convention(keypath_accessor_XXX)` convention in
SIL and lowering it in IRGen. This change unblocks the KeyPath projection
optimization while capturing subscript indices, and also makes it easier
to support WebAssembly target.

[^1]: https://github.com/apple/swift/pull/28799
[^2]: https://forums.swift.org/t/wasm-support/16087/21
2023-09-20 11:25:39 -07:00

640 lines
31 KiB
Swift

// RUN: %target-swift-emit-silgen -disable-availability-checking -parse-stdlib -module-name keypaths %s | %FileCheck %s
import Swift
struct S<T> {
var x: T
let y: String
var z: C<T>
var computed: C<T> { fatalError() }
var observed: C<T> { didSet { fatalError() } }
var reabstracted: () -> ()
}
class C<T> {
final var x: T
final let y: String
final var z: S<T>
var nonfinal: S<T>
var computed: S<T> { fatalError() }
var observed: S<T> { didSet { fatalError() } }
final var reabstracted: () -> ()
init() { fatalError() }
}
extension C {
var `extension`: S<T> { fatalError() }
}
protocol P {
var x: Int { get }
var y: String { get set }
}
extension P {
var z: String {
return y
}
var w: String {
get { return "" }
nonmutating set { }
}
}
struct T {
var a: (Int, String)
let b: (f: String, g: Int)
let c: (x: C<Int>, y: C<String>)
}
/* TODO: When we support superclass requirements on protocols, we should test
* this case as well.
protocol PoC : C<Int> {}
*/
// CHECK-LABEL: sil hidden [ossa] @{{.*}}storedProperties
func storedProperties<T>(_: T) {
// CHECK: keypath $WritableKeyPath<S<T>, T>, <τ_0_0> (root $S<τ_0_0>; stored_property #S.x : $τ_0_0) <T>
_ = \S<T>.x
// CHECK: keypath $KeyPath<S<T>, String>, <τ_0_0> (root $S<τ_0_0>; stored_property #S.y : $String) <T>
_ = \S<T>.y
// CHECK: keypath $ReferenceWritableKeyPath<S<T>, T>, <τ_0_0> (root $S<τ_0_0>; stored_property #S.z : $C<τ_0_0>; stored_property #C.x : $τ_0_0) <T>
_ = \S<T>.z.x
// CHECK: keypath $ReferenceWritableKeyPath<C<T>, T>, <τ_0_0> (root $C<τ_0_0>; stored_property #C.x : $τ_0_0) <T>
_ = \C<T>.x
// CHECK: keypath $KeyPath<C<T>, String>, <τ_0_0> (root $C<τ_0_0>; stored_property #C.y : $String) <T>
_ = \C<T>.y
// CHECK: keypath $ReferenceWritableKeyPath<C<T>, T>, <τ_0_0> (root $C<τ_0_0>; stored_property #C.z : $S<τ_0_0>; stored_property #S.x : $τ_0_0) <T>
_ = \C<T>.z.x
// CHECK: keypath $KeyPath<C<T>, String>, <τ_0_0> (root $C<τ_0_0>; stored_property #C.z : $S<τ_0_0>; stored_property #S.z : $C<τ_0_0>; stored_property #C.y : $String) <T>
_ = \C<T>.z.z.y
}
// CHECK-LABEL: sil hidden [ossa] @{{.*}}computedProperties
func computedProperties<T: P>(_: T) {
// CHECK: keypath $ReferenceWritableKeyPath<C<T>, S<T>>, <τ_0_0 where τ_0_0 : P> (
// CHECK-SAME: root $C<τ_0_0>;
// CHECK-SAME: settable_property $S<τ_0_0>,
// CHECK-SAME: id #C.nonfinal!getter : <T> (C<T>) -> () -> S<T>,
// CHECK-SAME: getter @$s8keypaths1CC8nonfinalAA1SVyxGvpAA1PRzlACyxGTK : $@convention(keypath_accessor_getter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed C<τ_0_0>) -> @out S<τ_0_0>,
// CHECK-SAME: setter @$s8keypaths1CC8nonfinalAA1SVyxGvpAA1PRzlACyxGTk : $@convention(keypath_accessor_setter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed S<τ_0_0>, @in_guaranteed C<τ_0_0>) -> ()
// CHECK-SAME: ) <T>
_ = \C<T>.nonfinal
// CHECK: keypath $KeyPath<C<T>, S<T>>, <τ_0_0 where τ_0_0 : P> (
// CHECK-SAME: root $C<τ_0_0>;
// CHECK-SAME: gettable_property $S<τ_0_0>,
// CHECK-SAME: id #C.computed!getter : <T> (C<T>) -> () -> S<T>,
// CHECK-SAME: getter @$s8keypaths1CC8computedAA1SVyxGvpAA1PRzlACyxGTK : $@convention(keypath_accessor_getter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed C<τ_0_0>) -> @out S<τ_0_0>
// CHECK-SAME: ) <T>
_ = \C<T>.computed
// CHECK: keypath $ReferenceWritableKeyPath<C<T>, S<T>>, <τ_0_0 where τ_0_0 : P> (
// CHECK-SAME: root $C<τ_0_0>;
// CHECK-SAME: settable_property $S<τ_0_0>,
// CHECK-SAME: id #C.observed!getter : <T> (C<T>) -> () -> S<T>,
// CHECK-SAME: getter @$s8keypaths1CC8observedAA1SVyxGvpAA1PRzlACyxGTK : $@convention(keypath_accessor_getter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed C<τ_0_0>) -> @out S<τ_0_0>,
// CHECK-SAME: setter @$s8keypaths1CC8observedAA1SVyxGvpAA1PRzlACyxGTk : $@convention(keypath_accessor_setter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed S<τ_0_0>, @in_guaranteed C<τ_0_0>) -> ()
// CHECK-SAME: ) <T>
_ = \C<T>.observed
_ = \C<T>.nonfinal.x
_ = \C<T>.computed.x
_ = \C<T>.observed.x
_ = \C<T>.z.computed
_ = \C<T>.z.observed
_ = \C<T>.observed.x
// CHECK: keypath $ReferenceWritableKeyPath<C<T>, () -> ()>, <τ_0_0 where τ_0_0 : P> (
// CHECK-SAME: root $C<τ_0_0>;
// CHECK-SAME: settable_property $() -> (),
// CHECK-SAME: id ##C.reabstracted,
// CHECK-SAME: getter @$s8keypaths1CC12reabstractedyycvpAA1PRzlACyxGTK : $@convention(keypath_accessor_getter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed C<τ_0_0>) -> @out @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <()>,
// CHECK-SAME: setter @$s8keypaths1CC12reabstractedyycvpAA1PRzlACyxGTk : $@convention(keypath_accessor_setter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <()>, @in_guaranteed C<τ_0_0>) -> ()
// CHECK-SAME: ) <T>
_ = \C<T>.reabstracted
// CHECK: keypath $KeyPath<S<T>, C<T>>, <τ_0_0 where τ_0_0 : P> (
// CHECK-SAME: root $S<τ_0_0>; gettable_property $C<τ_0_0>,
// CHECK-SAME: id @$s8keypaths1SV8computedAA1CCyxGvg : $@convention(method) <τ_0_0> (@in_guaranteed S<τ_0_0>) -> @owned C<τ_0_0>,
// CHECK-SAME: getter @$s8keypaths1SV8computedAA1CCyxGvpAA1PRzlACyxGTK : $@convention(keypath_accessor_getter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed S<τ_0_0>) -> @out C<τ_0_0>
// CHECK-SAME: ) <T>
_ = \S<T>.computed
// CHECK: keypath $WritableKeyPath<S<T>, C<T>>, <τ_0_0 where τ_0_0 : P> (
// CHECK-SAME: root $S<τ_0_0>;
// CHECK-SAME: settable_property $C<τ_0_0>,
// CHECK-SAME: id @$s8keypaths1SV8observedAA1CCyxGvg : $@convention(method) <τ_0_0> (@in_guaranteed S<τ_0_0>) -> @owned C<τ_0_0>,
// CHECK-SAME: getter @$s8keypaths1SV8observedAA1CCyxGvpAA1PRzlACyxGTK : $@convention(keypath_accessor_getter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed S<τ_0_0>) -> @out C<τ_0_0>,
// CHECK-SAME: setter @$s8keypaths1SV8observedAA1CCyxGvpAA1PRzlACyxGTk : $@convention(keypath_accessor_setter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed C<τ_0_0>, @inout S<τ_0_0>) -> ()
// CHECK-SAME: ) <T>
_ = \S<T>.observed
_ = \S<T>.z.nonfinal
_ = \S<T>.z.computed
_ = \S<T>.z.observed
_ = \S<T>.computed.x
_ = \S<T>.computed.y
// CHECK: keypath $WritableKeyPath<S<T>, () -> ()>, <τ_0_0 where τ_0_0 : P> (
// CHECK-SAME: root $S<τ_0_0>;
// CHECK-SAME: settable_property $() -> (),
// CHECK-SAME: id ##S.reabstracted,
// CHECK-SAME: getter @$s8keypaths1SV12reabstractedyycvpAA1PRzlACyxGTK : $@convention(keypath_accessor_getter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed S<τ_0_0>) -> @out @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <()>,
// CHECK-SAME: setter @$s8keypaths1SV12reabstractedyycvpAA1PRzlACyxGTk : $@convention(keypath_accessor_setter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed @callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <()>, @inout S<τ_0_0>) -> ()
// CHECK-SAME: ) <T>
_ = \S<T>.reabstracted
// CHECK: keypath $KeyPath<T, Int>, <τ_0_0 where τ_0_0 : P> (
// CHECK-SAME: root $τ_0_0;
// CHECK-SAME: gettable_property $Int,
// CHECK-SAME: id #P.x!getter : <Self where Self : P> (Self) -> () -> Int,
// CHECK-SAME: getter @$s8keypaths1PP1xSivpAaBRzlxTK : $@convention(keypath_accessor_getter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> @out Int
// CHECK-SAME: ) <T>
_ = \T.x
// CHECK: keypath $WritableKeyPath<T, String>, <τ_0_0 where τ_0_0 : P> (
// CHECK-SAME: root $τ_0_0;
// CHECK-SAME: settable_property $String,
// CHECK-SAME: id #P.y!getter : <Self where Self : P> (Self) -> () -> String,
// CHECK-SAME: getter @$s8keypaths1PP1ySSvpAaBRzlxTK : $@convention(keypath_accessor_getter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> @out String,
// CHECK-SAME: setter @$s8keypaths1PP1ySSvpAaBRzlxTk : $@convention(keypath_accessor_setter) <τ_0_0 where τ_0_0 : P> (@in_guaranteed String, @inout τ_0_0) -> ()
// CHECK-SAME: ) <T>
_ = \T.y
// CHECK: keypath $KeyPath<T, String>, <τ_0_0 where τ_0_0 : P> (
// CHECK-SAME: root $τ_0_0;
// CHECK-SAME: gettable_property $String,
// CHECK-SAME: id @$s8keypaths1PPAAE1zSSvg
_ = \T.z
}
struct Concrete: P {
var x: Int
var y: String
}
// CHECK-LABEL: sil hidden [ossa] @$s8keypaths35keyPathsWithSpecificGenericInstanceyyF
func keyPathsWithSpecificGenericInstance() {
// CHECK: keypath $KeyPath<Concrete, String>, (
// CHECK-SAME: gettable_property $String,
// CHECK-SAME: id @$s8keypaths1PPAAE1zSSvg
// CHECK-SAME: getter @$s8keypaths1PPAAE1zSSvpAA8ConcreteVTK : $@convention(keypath_accessor_getter) (@in_guaranteed Concrete) -> @out String
_ = \Concrete.z
_ = \S<Concrete>.computed
}
class AA<T> {
var a: Int { get { return 0 } set { } }
}
class BB<U, V>: AA<V> {
}
func keyPathForInheritedMember() {
_ = \BB<Int, String>.a
}
func keyPathForExistentialMember() {
_ = \P.x
_ = \P.y
_ = \P.z
_ = \P.w
}
struct OptionalFields {
var x: S<Int>?
}
struct OptionalFields2 {
var y: OptionalFields?
}
// CHECK-LABEL: sil hidden [ossa] @$s8keypaths18keyPathForOptionalyyF
func keyPathForOptional() {
// CHECK: keypath $WritableKeyPath<OptionalFields, S<Int>>, (
// CHECK-SAME: stored_property #OptionalFields.x : $Optional<S<Int>>;
// CHECK-SAME: optional_force : $S<Int>)
_ = \OptionalFields.x!
// CHECK: keypath $KeyPath<OptionalFields, Optional<String>>, (
// CHECK-SAME: stored_property #OptionalFields.x : $Optional<S<Int>>;
// CHECK-SAME: optional_chain : $S<Int>;
// CHECK-SAME: stored_property #S.y : $String;
// CHECK-SAME: optional_wrap : $Optional<String>)
_ = \OptionalFields.x?.y
// CHECK: keypath $KeyPath<OptionalFields2, Optional<S<Int>>>, (
// CHECK-SAME: root $OptionalFields2;
// CHECK-SAME: stored_property #OptionalFields2.y : $Optional<OptionalFields>;
// CHECK-SAME: optional_chain : $OptionalFields;
// CHECK-SAME: stored_property #OptionalFields.x : $Optional<S<Int>>)
_ = \OptionalFields2.y?.x
}
class StorageQualified {
weak var tooWeak: StorageQualified?
unowned var disowned: StorageQualified
init() { fatalError() }
}
final class FinalStorageQualified {
weak var tooWeak: StorageQualified?
unowned var disowned: StorageQualified
init() { fatalError() }
}
// CHECK-LABEL: sil hidden [ossa] @{{.*}}keyPathForStorageQualified
func keyPathForStorageQualified() {
// CHECK: = keypath $ReferenceWritableKeyPath<StorageQualified, Optional<StorageQualified>>,
// CHECK-SAME: settable_property $Optional<StorageQualified>, id #StorageQualified.tooWeak!getter
_ = \StorageQualified.tooWeak
// CHECK: = keypath $ReferenceWritableKeyPath<StorageQualified, StorageQualified>,
// CHECK-SAME: settable_property $StorageQualified, id #StorageQualified.disowned!getter
_ = \StorageQualified.disowned
// CHECK: = keypath $ReferenceWritableKeyPath<FinalStorageQualified, Optional<StorageQualified>>,
// CHECK-SAME: settable_property $Optional<StorageQualified>, id ##FinalStorageQualified.tooWeak
_ = \FinalStorageQualified.tooWeak
// CHECK: = keypath $ReferenceWritableKeyPath<FinalStorageQualified, StorageQualified>,
// CHECK-SAME: settable_property $StorageQualified, id ##FinalStorageQualified.disowned
_ = \FinalStorageQualified.disowned
}
struct IUOProperty {
var iuo: IUOBlob!
}
struct IUOBlob {
var x: Int
subscript(y: String) -> String {
get { return y }
set {}
}
}
// CHECK-LABEL: sil hidden [ossa] @{{.*}}11iuoKeyPaths
func iuoKeyPaths() {
// CHECK: = keypath $WritableKeyPath<IUOProperty, Int>,
// CHECK-SAME: stored_property #IUOProperty.iuo
// CHECK-SAME: optional_force
// CHECK-SAME: stored_property #IUOBlob.x
_ = \IUOProperty.iuo.x
// CHECK: = keypath $WritableKeyPath<IUOProperty, Int>,
// CHECK-SAME: stored_property #IUOProperty.iuo
// CHECK-SAME: optional_force
// CHECK-SAME: stored_property #IUOBlob.x
_ = \IUOProperty.iuo!.x
}
class Bass: Hashable {
static func ==(_: Bass, _: Bass) -> Bool { return false }
func hash(into hasher: inout Hasher) {}
}
class Treble: Bass { }
struct Subscripts<T> {
subscript() -> T {
get { fatalError() }
set { fatalError() }
}
subscript(generic x: T) -> T {
get { fatalError() }
set { fatalError() }
}
subscript(concrete x: String) -> String {
get { fatalError() }
set { fatalError() }
}
subscript(x: String, y: String) -> String {
get { fatalError() }
set { fatalError() }
}
subscript<U>(subGeneric z: U) -> U {
get { fatalError() }
set { fatalError() }
}
subscript(mutable x: T) -> T {
get { fatalError() }
set { fatalError() }
}
subscript(bass: Bass) -> Bass {
get { return bass }
set { }
}
}
struct SubscriptDefaults1 {
subscript(x: Int = 0) -> Int {
get { fatalError() }
set { fatalError() }
}
subscript(x: Int, y: Int, z: Int = 0) -> Int {
get { fatalError() }
set { fatalError() }
}
subscript(x: Bool, bool y: Bool = false) -> Bool {
get { fatalError() }
set { fatalError() }
}
subscript(bool x: Bool, y: Int, z: Int = 0) -> Int {
get { fatalError() }
set { fatalError() }
}
}
struct SubscriptDefaults2 {
subscript(x: Int? = nil) -> Int {
get { fatalError() }
set { fatalError() }
}
}
struct SubscriptDefaults3 {
subscript(x: Int = #line) -> Int {
get { fatalError() }
set { fatalError() }
}
}
struct SubscriptDefaults4 {
subscript<T : Numeric>(x x: T, y y: T = 0) -> T {
get { fatalError() }
set { fatalError() }
}
}
struct SubscriptDefaults5 {
subscript<T : ExpressibleByStringLiteral>(x x: T, y y: T = #function) -> T {
get { fatalError() }
set { fatalError() }
}
}
// CHECK-LABEL: sil hidden [ossa] @{{.*}}10subscripts1x1y1syx_q_SStSHRzSHR_r0_lF
func subscripts<T: Hashable, U: Hashable>(x: T, y: U, s: String) {
_ = \Subscripts<T>.[]
_ = \Subscripts<T>.[generic: x]
_ = \Subscripts<T>.[concrete: s]
_ = \Subscripts<T>.[s, s]
_ = \Subscripts<T>.[subGeneric: s]
_ = \Subscripts<T>.[subGeneric: x]
_ = \Subscripts<T>.[subGeneric: y]
_ = \Subscripts<U>.[]
_ = \Subscripts<U>.[generic: y]
_ = \Subscripts<U>.[concrete: s]
_ = \Subscripts<U>.[s, s]
_ = \Subscripts<U>.[subGeneric: s]
_ = \Subscripts<U>.[subGeneric: x]
_ = \Subscripts<U>.[subGeneric: y]
_ = \Subscripts<String>.[]
_ = \Subscripts<String>.[generic: s]
_ = \Subscripts<String>.[concrete: s]
_ = \Subscripts<String>.[s, s]
_ = \Subscripts<String>.[subGeneric: s]
_ = \Subscripts<String>.[subGeneric: x]
_ = \Subscripts<String>.[subGeneric: y]
_ = \Subscripts<T>.[s, s].count
_ = \Subscripts<T>.[Bass()]
_ = \Subscripts<T>.[Treble()]
_ = \SubscriptDefaults1.[]
_ = \SubscriptDefaults1.[0]
_ = \SubscriptDefaults1.[0, 0]
_ = \SubscriptDefaults1.[0, 0, 0]
_ = \SubscriptDefaults1.[false]
_ = \SubscriptDefaults1.[false, bool: false]
_ = \SubscriptDefaults1.[bool: false, 0]
_ = \SubscriptDefaults1.[bool: false, 0, 0]
_ = \SubscriptDefaults2.[]
_ = \SubscriptDefaults2.[0]
_ = \SubscriptDefaults3.[]
_ = \SubscriptDefaults3.[0]
_ = \SubscriptDefaults4.[x: 0]
_ = \SubscriptDefaults4.[x: 0, y: 0]
_ = \SubscriptDefaults5.[x: ""]
_ = \SubscriptDefaults5.[x: "", y: ""]
}
// CHECK-LABEL: sil hidden [ossa] @{{.*}}check_default_subscripts
func check_default_subscripts() {
// CHECK: [[INTX:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 0
// CHECK: [[IX:%[0-9]+]] = apply %{{[0-9]+}}([[INTX]], {{.*}}
// CHECK: [[INTY:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 0
// CHECK: [[IY:%[0-9]+]] = apply %{{[0-9]+}}([[INTY]], {{.*}}
// CHECK: [[KEYPATH:%[0-9]+]] = keypath $WritableKeyPath<SubscriptDefaults4, Int>, (root $SubscriptDefaults4; settable_property $Int, id @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluig : $@convention(method) <τ_0_0 where τ_0_0 : Numeric> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults4) -> @out τ_0_0, getter @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTK : $@convention(keypath_accessor_getter) (@in_guaranteed SubscriptDefaults4, @in_guaranteed (Int, Int)) -> @out Int, setter @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTk : $@convention(keypath_accessor_setter) (@in_guaranteed Int, @inout SubscriptDefaults4, @in_guaranteed (Int, Int)) -> (), indices [%$0 : $Int : $Int, %$1 : $Int : $Int], indices_equals @$sS2iTH : $@convention(keypath_accessor_equals) (@in_guaranteed (Int, Int), @in_guaranteed (Int, Int)) -> Bool, indices_hash @$sS2iTh : $@convention(keypath_accessor_hash) (@in_guaranteed (Int, Int)) -> Int) ([[IX]], [[IY]])
_ = \SubscriptDefaults4.[x: 0, y: 0]
// CHECK: [[INTINIT:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 0
// CHECK: [[I:%[0-9]+]] = apply %{{[0-9]+}}([[INTINIT]], {{.*}}
// CHECK: [[DFN:%[0-9]+]] = function_ref @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipfA0_ : $@convention(thin) <τ_0_0 where τ_0_0 : Numeric> () -> @out τ_0_0
// CHECK: [[ALLOC:%[0-9]+]] = alloc_stack $Int
// CHECK: apply [[DFN]]<Int>([[ALLOC]]) : $@convention(thin) <τ_0_0 where τ_0_0 : Numeric> () -> @out τ_0_0
// CHECK: [[LOAD:%[0-9]+]] = load [trivial] [[ALLOC]] : $*Int
// CHECK: [[KEYPATH:%[0-9]+]] = keypath $WritableKeyPath<SubscriptDefaults4, Int>, (root $SubscriptDefaults4; settable_property $Int, id @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluig : $@convention(method) <τ_0_0 where τ_0_0 : Numeric> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults4) -> @out τ_0_0, getter @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTK : $@convention(keypath_accessor_getter) (@in_guaranteed SubscriptDefaults4, @in_guaranteed (Int, Int)) -> @out Int, setter @$s8keypaths18SubscriptDefaults4V1x1yxx_xtcSjRzluipACSiTk : $@convention(keypath_accessor_setter) (@in_guaranteed Int, @inout SubscriptDefaults4, @in_guaranteed (Int, Int)) -> (), indices [%$0 : $Int : $Int, %$1 : $Int : $Int], indices_equals @$sS2iTH : $@convention(keypath_accessor_equals) (@in_guaranteed (Int, Int), @in_guaranteed (Int, Int)) -> Bool, indices_hash @$sS2iTh : $@convention(keypath_accessor_hash) (@in_guaranteed (Int, Int)) -> Int) ([[I]], [[LOAD]])
_ = \SubscriptDefaults4.[x: 0]
// CHECK: [[STRX_LIT:%[0-9]+]] = string_literal utf8 ""
// CHECK: [[STRX:%[0-9]+]] = apply %{{[0-9]+}}([[STRX_LIT]], {{.*}}
// CHECK: [[STRY_LIT:%[0-9]+]] = string_literal utf8 "check_default_subscripts()"
// CHECK: [[DEF_ARG:%[0-9]+]] = apply %{{[0-9]+}}([[STRY_LIT]], {{.*}}
// CHECK: keypath $WritableKeyPath<SubscriptDefaults5, String>, (root $SubscriptDefaults5; settable_property $String, id @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluig : $@convention(method) <τ_0_0 where τ_0_0 : ExpressibleByStringLiteral> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults5) -> @out τ_0_0, getter @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluipACSSTK : $@convention(keypath_accessor_getter) (@in_guaranteed SubscriptDefaults5, @in_guaranteed (String, String)) -> @out String, setter @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluipACSSTk : $@convention(keypath_accessor_setter) (@in_guaranteed String, @inout SubscriptDefaults5, @in_guaranteed (String, String)) -> (), indices [%$0 : $String : $String, %$1 : $String : $String], indices_equals @$sS2STH : $@convention(keypath_accessor_equals) (@in_guaranteed (String, String), @in_guaranteed (String, String)) -> Bool, indices_hash @$sS2STh : $@convention(keypath_accessor_hash) (@in_guaranteed (String, String)) -> Int) ([[STRX]], [[DEF_ARG]])
_ = \SubscriptDefaults5.[x: ""]
// CHECK: [[STRX_LIT:%[0-9]+]] = string_literal utf8 ""
// CHECK: [[STRX:%[0-9]+]] = apply %{{[0-9]+}}([[STRX_LIT]], {{.*}}
// CHECK: [[STRY_LIT:%[0-9]+]] = string_literal utf8 ""
// CHECK: [[STRY:%[0-9]+]] = apply %{{[0-9]+}}([[STRY_LIT]], {{.*}}
// CHECK: keypath $WritableKeyPath<SubscriptDefaults5, String>, (root $SubscriptDefaults5; settable_property $String, id @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluig : $@convention(method) <τ_0_0 where τ_0_0 : ExpressibleByStringLiteral> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, SubscriptDefaults5) -> @out τ_0_0, getter @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluipACSSTK : $@convention(keypath_accessor_getter) (@in_guaranteed SubscriptDefaults5, @in_guaranteed (String, String)) -> @out String, setter @$s8keypaths18SubscriptDefaults5V1x1yxx_xtcs26ExpressibleByStringLiteralRzluipACSSTk : $@convention(keypath_accessor_setter) (@in_guaranteed String, @inout SubscriptDefaults5, @in_guaranteed (String, String)) -> (), indices [%$0 : $String : $String, %$1 : $String : $String], indices_equals @$sS2STH : $@convention(keypath_accessor_equals) (@in_guaranteed (String, String), @in_guaranteed (String, String)) -> Bool, indices_hash @$sS2STh : $@convention(keypath_accessor_hash) (@in_guaranteed (String, String)) -> Int) ([[STRX]], [[STRY]])
_ = \SubscriptDefaults5.[x: "", y: ""]
}
struct SubscriptVariadic1 {
subscript(x: Int...) -> Int { x[0] }
}
struct SubscriptVariadic2 {
subscript<T : ExpressibleByStringLiteral>(x: T...) -> T { x[0] }
}
struct SubscriptVariadic3<T : ExpressibleByStringLiteral> {
subscript(x: T...) -> T { x[0] }
}
// CHECK-LABEL: sil hidden [ossa] @{{.*}}test_variadics
func test_variadics() {
// CHECK: [[ARR_COUNT:%[0-9]+]] = integer_literal $Builtin.Word, 3
// CHECK: [[FN_REF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF
// CHECK: [[MAKE_ARR:%[0-9]+]] = apply [[FN_REF]]<Int>([[ARR_COUNT]])
// CHECK: ([[ARR:%[0-9]+]], %{{[0-9]+}}) = destructure_tuple [[MAKE_ARR]] : $(Array<Int>, Builtin.RawPointer)
// CHECK: [[FIN_REF:%[0-9]+]] = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF
// CHECK: [[FIN_ARR:%[0-9]+]] = apply [[FIN_REF]]<Int>([[ARR]])
// CHECK: keypath $KeyPath<SubscriptVariadic1, Int>, (root $SubscriptVariadic1; gettable_property $Int, id @$s8keypaths18SubscriptVariadic1VyS2id_tcig : $@convention(method) (@guaranteed Array<Int>, SubscriptVariadic1) -> Int, getter @$s8keypaths18SubscriptVariadic1VyS2id_tcipACTK : $@convention(keypath_accessor_getter) (@in_guaranteed SubscriptVariadic1, @in_guaranteed Array<Int>) -> @out Int, indices [%$0 : $Array<Int> : $Array<Int>], indices_equals @$sSaySiGTH : $@convention(keypath_accessor_equals) (@in_guaranteed Array<Int>, @in_guaranteed Array<Int>) -> Bool, indices_hash @$sSaySiGTh : $@convention(keypath_accessor_hash) (@in_guaranteed Array<Int>) -> Int) ([[FIN_ARR]])
_ = \SubscriptVariadic1.[1, 2, 3]
// CHECK: [[ARR_COUNT:%[0-9]+]] = integer_literal $Builtin.Word, 1
// CHECK: [[FN_REF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF
// CHECK: [[MAKE_ARR:%[0-9]+]] = apply [[FN_REF]]<Int>([[ARR_COUNT]])
// CHECK: ([[ARR:%[0-9]+]], %{{[0-9]+}}) = destructure_tuple [[MAKE_ARR]] : $(Array<Int>, Builtin.RawPointer)
// CHECK: [[FIN_REF:%[0-9]+]] = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF
// CHECK: [[FIN_ARR:%[0-9]+]] = apply [[FIN_REF]]<Int>([[ARR]])
// CHECK: keypath $KeyPath<SubscriptVariadic1, Int>, (root $SubscriptVariadic1; gettable_property $Int, id @$s8keypaths18SubscriptVariadic1VyS2id_tcig : $@convention(method) (@guaranteed Array<Int>, SubscriptVariadic1) -> Int, getter @$s8keypaths18SubscriptVariadic1VyS2id_tcipACTK : $@convention(keypath_accessor_getter) (@in_guaranteed SubscriptVariadic1, @in_guaranteed Array<Int>) -> @out Int, indices [%$0 : $Array<Int> : $Array<Int>], indices_equals @$sSaySiGTH : $@convention(keypath_accessor_equals) (@in_guaranteed Array<Int>, @in_guaranteed Array<Int>) -> Bool, indices_hash @$sSaySiGTh : $@convention(keypath_accessor_hash) (@in_guaranteed Array<Int>) -> Int) ([[FIN_ARR]])
_ = \SubscriptVariadic1.[1]
// CHECK: [[ARR_COUNT:%[0-9]+]] = integer_literal $Builtin.Word, 0
// CHECK: [[FN_REF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF
// CHECK: [[MAKE_ARR:%[0-9]+]] = apply [[FN_REF]]<Int>([[ARR_COUNT]])
// CHECK: ([[ARR:%[0-9]+]], %{{[0-9]+}}) = destructure_tuple [[MAKE_ARR]] : $(Array<Int>, Builtin.RawPointer)
// CHECK: keypath $KeyPath<SubscriptVariadic1, Int>, (root $SubscriptVariadic1; gettable_property $Int, id @$s8keypaths18SubscriptVariadic1VyS2id_tcig : $@convention(method) (@guaranteed Array<Int>, SubscriptVariadic1) -> Int, getter @$s8keypaths18SubscriptVariadic1VyS2id_tcipACTK : $@convention(keypath_accessor_getter) (@in_guaranteed SubscriptVariadic1, @in_guaranteed Array<Int>) -> @out Int, indices [%$0 : $Array<Int> : $Array<Int>], indices_equals @$sSaySiGTH : $@convention(keypath_accessor_equals) (@in_guaranteed Array<Int>, @in_guaranteed Array<Int>) -> Bool, indices_hash @$sSaySiGTh : $@convention(keypath_accessor_hash) (@in_guaranteed Array<Int>) -> Int) ([[ARR]])
_ = \SubscriptVariadic1.[]
_ = \SubscriptVariadic2.["", "1"]
_ = \SubscriptVariadic2.[""]
// CHECK: [[ARR_COUNT:%[0-9]+]] = integer_literal $Builtin.Word, 2
// CHECK: [[FN_REF:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF
// CHECK: [[MAKE_ARR:%[0-9]+]] = apply [[FN_REF]]<String>([[ARR_COUNT]])
// CHECK: ([[ARR:%[0-9]+]], %{{[0-9]+}}) = destructure_tuple [[MAKE_ARR]] : $(Array<String>, Builtin.RawPointer)
// CHECK: [[FIN_REF:%[0-9]+]] = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF
// CHECK: [[FIN_ARR:%[0-9]+]] = apply [[FIN_REF]]<String>([[ARR]])
// CHECK: keypath $KeyPath<SubscriptVariadic2, String>, (root $SubscriptVariadic2; gettable_property $String, id @$s8keypaths18SubscriptVariadic2Vyxxd_tcs26ExpressibleByStringLiteralRzluig : $@convention(method) <τ_0_0 where τ_0_0 : ExpressibleByStringLiteral> (@guaranteed Array<τ_0_0>, SubscriptVariadic2) -> @out τ_0_0, getter @$s8keypaths18SubscriptVariadic2Vyxxd_tcs26ExpressibleByStringLiteralRzluipACSSTK : $@convention(keypath_accessor_getter) (@in_guaranteed SubscriptVariadic2, @in_guaranteed Array<String>) -> @out String, indices [%$0 : $Array<String> : $Array<String>], indices_equals @$sSaySSGTH : $@convention(keypath_accessor_equals) (@in_guaranteed Array<String>, @in_guaranteed Array<String>) -> Bool, indices_hash @$sSaySSGTh : $@convention(keypath_accessor_hash) (@in_guaranteed Array<String>) -> Int) ([[FIN_ARR]])
_ = \SubscriptVariadic2.["", #function]
_ = \SubscriptVariadic3<String>.[""]
_ = \SubscriptVariadic3<String>.["", "1"]
_ = \SubscriptVariadic3<String>.[]
}
// CHECK-LABEL: sil hidden [ossa] @{{.*}}subclass_generics
func subclass_generics<T: C<Int>, U: C<V>, V/*: PoC*/>(_: T, _: U, _: V) {
_ = \T.x
_ = \T.z
_ = \T.computed
_ = \T.extension
_ = \U.x
_ = \U.z
_ = \U.computed
_ = \U.extension
/*
_ = \V.x
_ = \V.z
_ = \V.computed
_ = \V.extension
*/
_ = \(C<Int> & P).x
_ = \(C<Int> & P).z
_ = \(C<Int> & P).computed
_ = \(C<Int> & P).extension
_ = \(C<V> & P).x
_ = \(C<V> & P).z
_ = \(C<V> & P).computed
_ = \(C<V> & P).extension
/* TODO: When we support superclass requirements on protocols, we should test
* this case as well.
_ = \PoC.x
_ = \PoC.z
_ = \PoC.computed
_ = \PoC.extension
*/
}
// CHECK-LABEL: sil hidden [ossa] @{{.*}}identity
func identity<T>(_: T) {
// CHECK: keypath $WritableKeyPath<T, T>, <τ_0_0> ({{.*}}root $τ_0_0) <T>
let _: WritableKeyPath<T, T> = \T.self
// CHECK: keypath $WritableKeyPath<Array<T>, Array<T>>, <τ_0_0> ({{.*}}root $Array<τ_0_0>) <T>
let _: WritableKeyPath<[T], [T]> = \[T].self
// CHECK: keypath $WritableKeyPath<String, String>, ({{.*}}root $String)
let _: WritableKeyPath<String, String> = \String.self
}
// CHECK-LABEL: sil hidden [ossa] @{{.*}}tuples
func tuples(_: T) {
// CHECK: keypath $WritableKeyPath<T, Int>, (root $T; stored_property #T.a : $(Int, String); tuple_element #0 : $Int)
let _: WritableKeyPath<T, Int> = \T.a.0
// CHECK: keypath $WritableKeyPath<T, String>, (root $T; stored_property #T.a : $(Int, String); tuple_element #1 : $String)
let _: WritableKeyPath<T, String> = \T.a.1
// CHECK: keypath $KeyPath<T, String>, (root $T; stored_property #T.b : $(f: String, g: Int); tuple_element #0 : $String)
let _: KeyPath<T, String> = \T.b.f
// CHECK: keypath $KeyPath<T, Int>, (root $T; stored_property #T.b : $(f: String, g: Int); tuple_element #1 : $Int)
let _: KeyPath<T, Int> = \T.b.g
// CHECK: keypath $KeyPath<T, C<Int>>, (root $T; stored_property #T.c : $(x: C<Int>, y: C<String>); tuple_element #0 : $C<Int>)
let _: KeyPath<T, C<Int>> = \T.c.x
// CHECK: keypath $KeyPath<T, C<String>>, (root $T; stored_property #T.c : $(x: C<Int>, y: C<String>); tuple_element #1 : $C<String>)
let _: KeyPath<T, C<String>> = \T.c.y
// CHECK: keypath $ReferenceWritableKeyPath<T, Int>, (root $T; stored_property #T.c : $(x: C<Int>, y: C<String>); tuple_element #0 : $C<Int>; stored_property #C.x : $Int)
let _: ReferenceWritableKeyPath<T, Int> = \T.c.x.x
// CHECK: keypath $KeyPath<T, String>, (root $T; stored_property #T.c : $(x: C<Int>, y: C<String>); tuple_element #0 : $C<Int>; stored_property #C.y : $String)
let _: KeyPath<T, String> = \T.c.x.y
typealias Thing = (type: Any.Type, fn: () -> ())
// CHECK: keypath $WritableKeyPath<(type: any Any.Type, fn: () -> ()), any Any.Type>, (root $(type: any Any.Type, fn: () -> ()); tuple_element #0 : $any Any.Type)
let _: WritableKeyPath<Thing, Any.Type> = \Thing.type
// CHECK: keypath $WritableKeyPath<(type: any Any.Type, fn: () -> ()), () -> ()>, (root $(type: any Any.Type, fn: () -> ()); tuple_element #1 : $() -> ())
let _: WritableKeyPath<Thing, () -> ()> = \Thing.fn
}
// CHECK-LABEL: sil hidden [ossa] @{{.*}}tuples_generic
func tuples_generic<T, U, V>(_: T, _: U, _: V) {
typealias TUC = (T, U, C<V>)
// CHECK: keypath $WritableKeyPath<(T, U, C<V>), T>, <τ_0_0, τ_0_1, τ_0_2> (root $(τ_0_0, τ_0_1, C<τ_0_2>); tuple_element #0 : $τ_0_0) <T, U, V>
let _: WritableKeyPath<TUC, T> = \TUC.0
// CHECK: keypath $WritableKeyPath<(T, U, C<V>), U>, <τ_0_0, τ_0_1, τ_0_2> (root $(τ_0_0, τ_0_1, C<τ_0_2>); tuple_element #1 : $τ_0_1) <T, U, V>
let _: WritableKeyPath<TUC, U> = \TUC.1
// CHECK: keypath $ReferenceWritableKeyPath<(T, U, C<V>), V>, <τ_0_0, τ_0_1, τ_0_2> (root $(τ_0_0, τ_0_1, C<τ_0_2>); tuple_element #2 : $C<τ_0_2>; stored_property #C.x : $τ_0_2) <T, U, V>
let _: ReferenceWritableKeyPath<TUC, V> = \TUC.2.x
// CHECK: keypath $KeyPath<(T, U, C<V>), String>, <τ_0_0, τ_0_1, τ_0_2> (root $(τ_0_0, τ_0_1, C<τ_0_2>); tuple_element #2 : $C<τ_0_2>; stored_property #C.y : $String) <T, U, V>
let _: KeyPath<TUC, String> = \TUC.2.y
}
protocol DefineSomeType {
associatedtype SomeType
func defineSome() -> SomeType
}
protocol A {}
extension Int : A {}
struct TestKeyPathWithSomeType : DefineSomeType {
func defineSome() -> some A {
return 0
}
func testKeyPathWithSome() {
_ = \S<SomeType>.y
_ = \S<SomeType>.z.x
_ = \C<SomeType>.x
_ = \C<SomeType>.y
_ = \C<SomeType>.z.x
_ = \C<SomeType>.z.z.y
_ = \C<SomeType>.nonfinal
_ = \C<SomeType>.computed
_ = \C<SomeType>.observed
_ = \C<SomeType>.nonfinal.x
_ = \C<SomeType>.computed.x
_ = \C<SomeType>.observed.x
_ = \C<SomeType>.z.computed
_ = \C<SomeType>.z.observed
_ = \C<SomeType>.observed.x
_ = \C<SomeType>.reabstracted
_ = \S<SomeType>.computed
_ = \S<SomeType>.observed
_ = \S<SomeType>.z.nonfinal
_ = \S<SomeType>.z.computed
_ = \S<SomeType>.z.observed
_ = \S<SomeType>.computed.x
_ = \S<SomeType>.computed.y
_ = \S<SomeType>.reabstracted
}
}