mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Added more tests
Testing SILGen & IRGen tuple keypath generation Added tuple element type check in SILVerifier
This commit is contained in:
committed by
Andrea Tomarelli
parent
a0ed29d326
commit
d7324b977e
@@ -361,11 +361,7 @@ std::pair<bool, Expr *> SemaAnnotator::walkToExprPre(Expr *E) {
|
||||
break;
|
||||
}
|
||||
|
||||
case KeyPathExpr::Component::Kind::TupleElement: {
|
||||
llvm_unreachable("[technicated]");
|
||||
break;
|
||||
}
|
||||
|
||||
case KeyPathExpr::Component::Kind::TupleElement:
|
||||
case KeyPathExpr::Component::Kind::Invalid:
|
||||
case KeyPathExpr::Component::Kind::UnresolvedProperty:
|
||||
case KeyPathExpr::Component::Kind::UnresolvedSubscript:
|
||||
|
||||
@@ -389,10 +389,18 @@ void verifyKeyPathComponent(SILModule &M,
|
||||
"invalid baseTy, should have been a TupleType");
|
||||
|
||||
auto tupleTy = loweredBaseTy.getAs<TupleType>();
|
||||
|
||||
require(component.getTupleIndex() < tupleTy->getNumElements(),
|
||||
auto eltIdx = component.getTupleIndex();
|
||||
|
||||
require(eltIdx < tupleTy->getNumElements(),
|
||||
"invalid element index, greater than # of tuple elements");
|
||||
|
||||
auto eltTy = tupleTy->getElementType(eltIdx)
|
||||
->getReferenceStorageReferent()
|
||||
->getCanonicalType();
|
||||
|
||||
require(eltTy == componentTy,
|
||||
"tuple element type should match the type of the component");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3049,7 +3049,7 @@ namespace {
|
||||
}
|
||||
|
||||
case KeyPathExpr::Component::Kind::TupleElement: {
|
||||
llvm_unreachable("[technicated]");
|
||||
llvm_unreachable("not implemented");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -4334,7 +4334,7 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
|
||||
break;
|
||||
|
||||
case KeyPathExpr::Component::Kind::TupleElement:
|
||||
llvm_unreachable("[technicated]");
|
||||
llvm_unreachable("not implemented");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,13 @@ public class C2: C1 {
|
||||
public var reabstracted: () -> ()
|
||||
}
|
||||
|
||||
public struct T {
|
||||
public var a: (Int, String)
|
||||
public let b: (String, Int)
|
||||
public var c: (f: Int, g: String)
|
||||
public let d: (x: String, y: Int)
|
||||
}
|
||||
|
||||
sil_vtable C {}
|
||||
sil_vtable C1 {}
|
||||
sil_vtable C2 {}
|
||||
@@ -180,6 +187,121 @@ sil_vtable C2 {}
|
||||
// looping in the compiler at one point.
|
||||
// CHECK: [[KP_M:@keypath.*]] = private global <{ {{.*}} }> <{
|
||||
|
||||
// -- %t0: T.a.0
|
||||
// CHECK: [[KP_T0:@keypath(\..*)?]] = private global <{ {{.*}} }> <{
|
||||
// CHECK-SAME: [[WORD]]* @keypath_once
|
||||
// CHECK-SAME: @"symbolic
|
||||
// CHECK-SAME: @"symbolic
|
||||
// -- instantiable in-line, size 12
|
||||
// CHECK-SAME: <i32 0x8000_000c>,
|
||||
// -- offset of T.a, mutable
|
||||
// CHECK-SAME: <i32 0x0180_0000>,
|
||||
// CHECK: @"symbolic
|
||||
// -- tuple element #0 of T.a
|
||||
// CHECK-SAME: <i32 0x0180_0000> }>
|
||||
|
||||
// -- %t1: T.a.1
|
||||
// CHECK: [[KP_T1:@keypath(\..*)?]] = private global <{ {{.*}} }> <{
|
||||
// CHECK-SAME: [[WORD]]* @keypath_once
|
||||
// CHECK-SAME: @"symbolic
|
||||
// CHECK-SAME: @"symbolic
|
||||
// -- instantiable in-line, size 12
|
||||
// CHECK-SAME: <i32 0x8000_000c>,
|
||||
// -- offset of T.a, mutable
|
||||
// CHECK-SAME: <i32 0x0180_0000>,
|
||||
// CHECK: @"symbolic
|
||||
// -- tuple element #1 of T.a
|
||||
// CHECK-32-SAME: <i32 0x0180_0004> }>
|
||||
// CHECK-64-SAME: <i32 0x0180_0008> }>
|
||||
|
||||
// -- %t2: T.b.0
|
||||
// CHECK: [[KP_T2:@keypath(\..*)?]] = private global <{ {{.*}} }> <{
|
||||
// CHECK-SAME: [[WORD]]* @keypath_once
|
||||
// CHECK-SAME: @"symbolic
|
||||
// CHECK-SAME: @"symbolic
|
||||
// -- instantiable in-line, size 12
|
||||
// CHECK-SAME: <i32 0x8000_000c>,
|
||||
// -- offset of T.b
|
||||
// CHECK-32-SAME: <i32 0x0100_000c>,
|
||||
// CHECK-64-SAME: <i32 0x0100_0018>,
|
||||
// CHECK: @"symbolic
|
||||
// -- tuple element #0 of T.b
|
||||
// CHECK-SAME: <i32 0x0180_0000> }>
|
||||
|
||||
// -- %t3: T.b.1
|
||||
// CHECK: [[KP_T3:@keypath(\..*)?]] = private global <{ {{.*}} }> <{
|
||||
// CHECK-SAME: [[WORD]]* @keypath_once
|
||||
// CHECK-SAME: @"symbolic
|
||||
// CHECK-SAME: @"symbolic
|
||||
// -- instantiable in-line, size 12
|
||||
// CHECK-SAME: <i32 0x8000_000c>,
|
||||
// -- offset of T.b
|
||||
// CHECK-32-SAME: <i32 0x0100_000c>,
|
||||
// CHECK-64-SAME: <i32 0x0100_0018>,
|
||||
// CHECK: @"symbolic
|
||||
// -- tuple element #1 of T.b
|
||||
// CHECK-32-SAME: <i32 0x0180_0008> }>
|
||||
// CHECK-64-SAME: <i32 0x0180_0010> }>
|
||||
|
||||
// -- %t4: T.c.f
|
||||
// CHECK: [[KP_T4:@keypath(\..*)?]] = private global <{ {{.*}} }> <{
|
||||
// CHECK-SAME: [[WORD]]* @keypath_once
|
||||
// CHECK-SAME: @"symbolic
|
||||
// CHECK-SAME: @"symbolic
|
||||
// -- instantiable in-line, size 12
|
||||
// CHECK-SAME: <i32 0x8000_000c>,
|
||||
// -- offset of T.f, mutable
|
||||
// CHECK-32-SAME: <i32 0x0180_0018>,
|
||||
// CHECK-64-SAME: <i32 0x0180_0030>,
|
||||
// CHECK: @"symbolic
|
||||
// -- tuple element #0 of T.f
|
||||
// CHECK-SAME: <i32 0x0180_0000> }>
|
||||
|
||||
// -- %t5: T.c.g
|
||||
// CHECK: [[KP_T5:@keypath(\..*)?]] = private global <{ {{.*}} }> <{
|
||||
// CHECK-SAME: [[WORD]]* @keypath_once
|
||||
// CHECK-SAME: @"symbolic
|
||||
// CHECK-SAME: @"symbolic
|
||||
// -- instantiable in-line, size 12
|
||||
// CHECK-SAME: <i32 0x8000_000c>,
|
||||
// -- offset of T.f, mutable
|
||||
// CHECK-32-SAME: <i32 0x0180_0018>,
|
||||
// CHECK-64-SAME: <i32 0x0180_0030>,
|
||||
// CHECK: @"symbolic
|
||||
// -- tuple element #1 of T.f
|
||||
// CHECK-32-SAME: <i32 0x0180_0004> }>
|
||||
// CHECK-64-SAME: <i32 0x0180_0008> }>
|
||||
|
||||
// -- %t6: T.d.x
|
||||
// CHECK: [[KP_T6:@keypath(\..*)?]] = private global <{ {{.*}} }> <{
|
||||
// CHECK-SAME: [[WORD]]* @keypath_once
|
||||
// CHECK-SAME: @"symbolic
|
||||
// CHECK-SAME: @"symbolic
|
||||
// -- instantiable in-line, size 12
|
||||
// CHECK-SAME: <i32 0x8000_000c>,
|
||||
// -- offset of T.d
|
||||
// CHECK-32-SAME: <i32 0x0100_0024>,
|
||||
// CHECK-64-SAME: <i32 0x0100_0048>,
|
||||
// CHECK: @"symbolic
|
||||
// -- tuple element #0 of T.d
|
||||
// CHECK-32-SAME: <i32 0x0180_0000> }>
|
||||
// CHECK-64-SAME: <i32 0x0180_0000> }>
|
||||
|
||||
// -- %t7: T.d.y
|
||||
// CHECK: [[KP_T7:@keypath(\..*)?]] = private global <{ {{.*}} }> <{
|
||||
// CHECK-SAME: [[WORD]]* @keypath_once
|
||||
// CHECK-SAME: @"symbolic
|
||||
// CHECK-SAME: @"symbolic
|
||||
// -- instantiable in-line, size 12
|
||||
// CHECK-SAME: <i32 0x8000_000c>,
|
||||
// -- offset of T.d
|
||||
// CHECK-32-SAME: <i32 0x0100_0024>,
|
||||
// CHECK-64-SAME: <i32 0x0100_0048>,
|
||||
// CHECK: @"symbolic
|
||||
// -- tuple element #0 of T.d
|
||||
// CHECK-32-SAME: <i32 0x0180_0008> }>
|
||||
// CHECK-64-SAME: <i32 0x0180_0010> }>
|
||||
|
||||
// -- %i: Gen<A>.x
|
||||
// CHECK: [[KP_I:@keypath(\..*)?]] = private global <{ {{.*}} }> <{
|
||||
// CHECK-SAME: i32 0
|
||||
@@ -236,6 +358,26 @@ entry:
|
||||
%m = keypath $KeyPath<S, () -> ()>, (root $S; settable_property $() -> (), id ##S.reabstracted, getter @m_get : $@convention(thin) (@in_guaranteed S) -> @out @callee_guaranteed () -> @out (), setter @m_set : $@convention(thin) (@in_guaranteed @callee_guaranteed () -> @out (), @inout S) -> ())
|
||||
%m2 = keypath $KeyPath<C2, () -> ()>, (root $C2; settable_property $() -> (), id ##C2.reabstracted, getter @m2_get : $@convention(thin) (@in_guaranteed C2) -> @out @callee_guaranteed () -> @out (), setter @m2_set : $@convention(thin) (@in_guaranteed @callee_guaranteed () -> @out (), @inout C2) -> ())
|
||||
|
||||
// CHECK: call %swift.refcounted* @swift_getKeyPath(i8* bitcast ({{.*}} [[KP_T0]] to i8*), i8* undef)
|
||||
%t0 = keypath $KeyPath<T, Int>, (root $T; stored_property #T.a : $(Int, String); tuple_element #0 : $Int)
|
||||
// CHECK: call %swift.refcounted* @swift_getKeyPath(i8* bitcast ({{.*}} [[KP_T1]] to i8*), i8* undef)
|
||||
%t1 = keypath $KeyPath<T, String>, (root $T; stored_property #T.a : $(Int, String); tuple_element #1 : $String)
|
||||
|
||||
// CHECK: call %swift.refcounted* @swift_getKeyPath(i8* bitcast ({{.*}} [[KP_T2]] to i8*), i8* undef)
|
||||
%t2 = keypath $KeyPath<T, String>, (root $T; stored_property #T.b : $(String, Int); tuple_element #0 : $String)
|
||||
// CHECK: call %swift.refcounted* @swift_getKeyPath(i8* bitcast ({{.*}} [[KP_T3]] to i8*), i8* undef)
|
||||
%t3 = keypath $KeyPath<T, Int>, (root $T; stored_property #T.b : $(String, Int); tuple_element #1 : $Int)
|
||||
|
||||
// CHECK: call %swift.refcounted* @swift_getKeyPath(i8* bitcast ({{.*}} [[KP_T4]] to i8*), i8* undef)
|
||||
%t4 = keypath $KeyPath<T, Int>, (root $T; stored_property #T.c : $(f: Int, g: String); tuple_element #0 : $Int)
|
||||
// CHECK: call %swift.refcounted* @swift_getKeyPath(i8* bitcast ({{.*}} [[KP_T5]] to i8*), i8* undef)
|
||||
%t5 = keypath $KeyPath<T, String>, (root $T; stored_property #T.c : $(f: Int, g: String); tuple_element #1 : $String)
|
||||
|
||||
// CHECK: call %swift.refcounted* @swift_getKeyPath(i8* bitcast ({{.*}} [[KP_T6]] to i8*), i8* undef)
|
||||
%t6 = keypath $KeyPath<T, String>, (root $T; stored_property #T.d : $(x: String, y: Int); tuple_element #0 : $String)
|
||||
// CHECK: call %swift.refcounted* @swift_getKeyPath(i8* bitcast ({{.*}} [[KP_T7]] to i8*), i8* undef)
|
||||
%t7 = keypath $KeyPath<T, Int>, (root $T; stored_property #T.d : $(x: String, y: Int); tuple_element #1 : $Int)
|
||||
|
||||
return undef : $()
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,13 @@ public struct External<T> {
|
||||
subscript<U: Hashable>(ro _: U) -> T { get }
|
||||
}
|
||||
|
||||
struct T {
|
||||
var x: (Int, Int)
|
||||
var y: (a: Int, b: Int, c: Int)
|
||||
let z: (Int, Int)
|
||||
let w: (Int, C)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil shared [ossa] @stored_properties
|
||||
sil shared [ossa] @stored_properties : $@convention(thin) () -> () {
|
||||
entry:
|
||||
@@ -69,6 +76,21 @@ entry:
|
||||
return undef : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil shared [ossa] @tuple_elements
|
||||
sil shared [ossa] @tuple_elements : $@convention(thin) () -> () {
|
||||
entry:
|
||||
// CHECK: keypath $WritableKeyPath<T, Int>, (root $T; stored_property #T.x : $(Int, Int); tuple_element #0 : $Int)
|
||||
%a = keypath $WritableKeyPath<T, Int>, (root $T; stored_property #T.x : $(Int, Int); tuple_element #0 : $Int)
|
||||
// CHECK: keypath $WritableKeyPath<T, Int>, (root $T; stored_property #T.y : $(a: Int, b: Int, c: Int); tuple_element #2 : $Int)
|
||||
%b = keypath $WritableKeyPath<T, Int>, (root $T; stored_property #T.y : $(a: Int, b: Int, c: Int); tuple_element #2 : $Int)
|
||||
// CHECK: keypath $KeyPath<T, Int>, (root $T; stored_property #T.z : $(Int, Int); tuple_element #1 : $Int)
|
||||
%c = keypath $KeyPath<T, Int>, (root $T; stored_property #T.z : $(Int, Int); tuple_element #1 : $Int)
|
||||
// CHECK: keypath $ReferenceWritableKeyPath<T, C>, (root $T; stored_property #T.w : $(Int, C); tuple_element #1 : $C)
|
||||
%d = keypath $ReferenceWritableKeyPath<T, C>, (root $T; stored_property #T.w : $(Int, C); tuple_element #1 : $C)
|
||||
|
||||
return undef : $()
|
||||
}
|
||||
|
||||
sil @id_a : $@convention(thin) () -> ()
|
||||
sil @get_s_int : $@convention(thin) (@in_guaranteed S) -> @out Int
|
||||
sil @set_s_int : $@convention(thin) (@in_guaranteed Int, @in_guaranteed S) -> ()
|
||||
|
||||
@@ -89,6 +89,10 @@ entry:
|
||||
%a = keypath $WritableKeyPath<T, Int>, (root $T; stored_property #T.x : $(Int, Int); tuple_element #0 : $Int)
|
||||
// CHECK: keypath $WritableKeyPath<T, Int>, (root $T; stored_property #T.y : $(a: Int, b: Int, c: Int); tuple_element #2 : $Int)
|
||||
%b = keypath $WritableKeyPath<T, Int>, (root $T; stored_property #T.y : $(a: Int, b: Int, c: Int); tuple_element #2 : $Int)
|
||||
// CHECK: keypath $KeyPath<T, Int>, (root $T; stored_property #T.z : $(Int, Int); tuple_element #1 : $Int)
|
||||
%c = keypath $KeyPath<T, Int>, (root $T; stored_property #T.z : $(Int, Int); tuple_element #1 : $Int)
|
||||
// CHECK: keypath $ReferenceWritableKeyPath<T, C>, (root $T; stored_property #T.w : $(Int, C); tuple_element #1 : $C)
|
||||
%d = keypath $ReferenceWritableKeyPath<T, C>, (root $T; stored_property #T.w : $(Int, C); tuple_element #1 : $C)
|
||||
|
||||
return undef : $()
|
||||
}
|
||||
@@ -178,8 +182,8 @@ entry:
|
||||
sil [serialized] [ossa] @serialize_all : $@convention(thin) () -> () {
|
||||
entry:
|
||||
%0 = function_ref @stored_properties : $@convention(thin) () -> ()
|
||||
%1 = function_ref @tuple_elements : $@convention(thin) () -> ()
|
||||
%2 = function_ref @stored_properties_generic : $@convention(thin) <D: P, E: Q, F: R> () -> ()
|
||||
%1 = function_ref @stored_properties_generic : $@convention(thin) <D: P, E: Q, F: R> () -> ()
|
||||
%2 = function_ref @tuple_elements : $@convention(thin) () -> ()
|
||||
%3 = function_ref @computed_properties : $@convention(thin) () -> ()
|
||||
%4 = function_ref @computed_properties_generic : $@convention(thin) <D: P, E: Q, F: R> () -> ()
|
||||
%5 = function_ref @optional : $@convention(thin) () -> ()
|
||||
|
||||
@@ -43,6 +43,12 @@ extension P {
|
||||
}
|
||||
}
|
||||
|
||||
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> {}
|
||||
@@ -396,3 +402,23 @@ func identity<T>(_: T) {
|
||||
let _: WritableKeyPath<String, String> = \String.self
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden @{{.*}}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
|
||||
}
|
||||
|
||||
@@ -688,6 +688,15 @@ struct NonOffsetableProperties {
|
||||
var z: Int { return 0 }
|
||||
}
|
||||
|
||||
struct TupleProperties {
|
||||
// unlabeled
|
||||
var a: (Int, String)
|
||||
// labeled
|
||||
let b: (x: String, y: Int)
|
||||
// reference writable
|
||||
let c: (m: C<Int>, n: C<String>)
|
||||
}
|
||||
|
||||
func getIdentityKeyPathOfType<T>(_: T.Type) -> KeyPath<T, T> {
|
||||
return \.self
|
||||
}
|
||||
@@ -710,6 +719,17 @@ keyPath.test("offsets") {
|
||||
|
||||
expectEqual(SLayout.offset(of: \.self), 0)
|
||||
expectEqual(SLayout.offset(of: getIdentityKeyPathOfType(S<Int>.self)), 0)
|
||||
|
||||
let TPLayout = MemoryLayout<TupleProperties>.self
|
||||
expectEqual(TPLayout.offset(of: \TupleProperties.a), 0)
|
||||
expectEqual(TPLayout.offset(of: \TupleProperties.a.0), 0)
|
||||
expectEqual(TPLayout.offset(of: \TupleProperties.a.1), 8)
|
||||
expectEqual(TPLayout.offset(of: \TupleProperties.b), 24)
|
||||
expectEqual(TPLayout.offset(of: \TupleProperties.b.x), 24)
|
||||
expectEqual(TPLayout.offset(of: \TupleProperties.b.y), 40)
|
||||
expectEqual(TPLayout.offset(of: \TupleProperties.c), 48)
|
||||
expectEqual(TPLayout.offset(of: \TupleProperties.c.m), 48)
|
||||
expectEqual(TPLayout.offset(of: \TupleProperties.c.n), 56)
|
||||
}
|
||||
|
||||
keyPath.test("identity key path") {
|
||||
@@ -745,6 +765,38 @@ keyPath.test("identity key path") {
|
||||
expectEqual(x[keyPath: valueKey3], 679)
|
||||
}
|
||||
|
||||
keyPath.test("tuple key path") {
|
||||
let t0 = \TupleProperties.a.0
|
||||
expectNotNil(t0 as? KeyPath<TupleProperties, Int>)
|
||||
expectNotNil(t0 as? WritableKeyPath<TupleProperties, Int>)
|
||||
expectNil(t0 as? ReferenceWritableKeyPath<TupleProperties, Int>)
|
||||
|
||||
let t1 = \TupleProperties.a.1
|
||||
expectNotNil(t1 as? KeyPath<TupleProperties, String>)
|
||||
expectNotNil(t1 as? WritableKeyPath<TupleProperties, String>)
|
||||
expectNil(t1 as? ReferenceWritableKeyPath<TupleProperties, String>)
|
||||
|
||||
let t2 = \TupleProperties.b.x
|
||||
expectNotNil(t2 as? KeyPath<TupleProperties, String>)
|
||||
expectNil(t2 as? WritableKeyPath<TupleProperties, String>)
|
||||
expectNil(t2 as? ReferenceWritableKeyPath<TupleProperties, String>)
|
||||
|
||||
let t3 = \TupleProperties.b.y
|
||||
expectNotNil(t3 as? KeyPath<TupleProperties, Int>)
|
||||
expectNil(t3 as? WritableKeyPath<TupleProperties, Int>)
|
||||
expectNil(t3 as? ReferenceWritableKeyPath<TupleProperties, Int>)
|
||||
|
||||
let t4 = \TupleProperties.c.m
|
||||
expectNotNil(t4 as? KeyPath<TupleProperties, C<Int>>)
|
||||
expectNil(t4 as? WritableKeyPath<TupleProperties, C<Int>>)
|
||||
expectNil(t4 as? ReferenceWritableKeyPath<TupleProperties, C<Int>>)
|
||||
|
||||
let t5 = \TupleProperties.c.n.z
|
||||
expectNotNil(t5 as? KeyPath<TupleProperties, String>)
|
||||
expectNotNil(t5 as? WritableKeyPath<TupleProperties, String>)
|
||||
expectNotNil(t5 as? ReferenceWritableKeyPath<TupleProperties, String>)
|
||||
}
|
||||
|
||||
keyPath.test("let-ness") {
|
||||
expectNil(\C<Int>.immutable as? ReferenceWritableKeyPath)
|
||||
expectNotNil(\C<Int>.secretlyMutable as? ReferenceWritableKeyPath)
|
||||
|
||||
Reference in New Issue
Block a user