Added more tests

Testing SILGen & IRGen tuple keypath generation
Added tuple element type check in SILVerifier
This commit is contained in:
technicated
2018-12-29 16:55:19 +01:00
committed by Andrea Tomarelli
parent a0ed29d326
commit d7324b977e
9 changed files with 261 additions and 11 deletions

View File

@@ -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:

View File

@@ -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;
}
}

View File

@@ -3049,7 +3049,7 @@ namespace {
}
case KeyPathExpr::Component::Kind::TupleElement: {
llvm_unreachable("[technicated]");
llvm_unreachable("not implemented");
break;
}

View File

@@ -4334,7 +4334,7 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
break;
case KeyPathExpr::Component::Kind::TupleElement:
llvm_unreachable("[technicated]");
llvm_unreachable("not implemented");
break;
}
}

View File

@@ -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 : $()
}

View File

@@ -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) -> ()

View File

@@ -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) () -> ()

View File

@@ -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
}

View File

@@ -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)