Files
swift-mirror/test/IRGen/objc_protocols.swift
Slava Pestov 60f437abe1 IRGen: Change witness_method calling convention to take a witness table
This is another incremental step toward protocol resilience.

To support resiliently adding requirements with default implementations,
we need to emit the witness thunk for each default requirement once,
and share it between conformances.

However, the body of the witness thunk can call witness methods from
the conformance of <Self : P>. Formerly, witness thunks were only emitted
with a concrete Self type, so any calls were resolved statically.

Now that Self can be abstract in a witness thunk signature, we have to
pass in the witness table and do the necessary gymnastics on both sides
of the call.

At the call site, the witness table is either abstract, concrete, or
undefined, as follows:

- If the unsubstituted Self type is concrete in the witness method
  signature, no witness table is necessary; this is the case of a
  concrete (non-default) witness thunk.

- If the unsubstituted Self type is abstract and the substituted Self
  type is concrete, the witness table is accessed via direct reference.

- If the unsubstituted Self type is abstract and the substituted Self
  type is also abstract, the witness table comes from type metadata
  that was passed in to the function where the call is taking place.

Inside the body of the witness method thunk, we only bind the witness
table if Self is an abstract type; this rules out the first case above,
where the witness table is not needed and cannot be provided by the
caller.

The result of a SIL witness_method instruction now lowers as an
explosion containing two values, the function pointer itself and
the witness table.

Similarly, partial application thunks now grab the witness table and
package it up in the context.

Special care is taken to support function_ref + apply and
function_ref + partial_apply of @convention(witness_method) callees;
here, we can hit the case where we don't know the original conformance
because the callee is concrete, in which case we just pass in a null
pointer as the witness table.

Witness thunks with an abstract Self currently only work for protocols
without any associated type requirements; to support those, we need
to be able to fulfill associated type metadata from the witness
table for the <Self : P> conformance. This will be addressed as part
of @rjmccall's calling convention work.

Also I didn't make any attempt to support this for @objc protocols that
do not have a witness table. In this case, the extra parameter is not
necessary since we can perform dynamic dispatch on the 'self' value to
call requirements; however, @objc protocols will not support default
implementations, at least not in the near-term.
2016-02-16 01:46:18 -08:00

206 lines
10 KiB
Swift

// RUN: rm -rf %t && mkdir %t
// RUN: %build-irgen-test-overlays
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -emit-module -o %t %S/Inputs/objc_protocols_Bas.swift
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-ir -disable-objc-attr-requires-foundation-module | FileCheck %s
// REQUIRES: CPU=x86_64
// REQUIRES: objc_interop
import gizmo
import objc_protocols_Bas
// -- Protocol "Frungible" inherits only objc protocols and should have no
// out-of-line inherited witnesses in its witness table.
// CHECK: [[ZIM_FRUNGIBLE_WITNESS:@_TWPC14objc_protocols3ZimS_9FrungibleS_]] = hidden constant [1 x i8*] [
// CHECK: i8* bitcast (void (%C14objc_protocols3Zim*, %swift.type*, i8**)* @_TTWC14objc_protocols3ZimS_9FrungibleS_FS1_6frungefT_T_ to i8*)
// CHECK: ]
protocol Ansible {
func anse()
}
class Foo : NSRuncing, NSFunging, Ansible {
@objc func runce() {}
@objc func funge() {}
@objc func foo() {}
func anse() {}
}
// CHECK: @_INSTANCE_METHODS__TtC14objc_protocols3Foo = private constant { i32, i32, [3 x { i8*, i8*, i8* }] } {
// CHECK: i32 24, i32 3,
// CHECK: [3 x { i8*, i8*, i8* }] [
// CHECK: { i8*, i8*, i8* } { i8* getelementptr inbounds ([6 x i8], [6 x i8]* @"\01L_selector_data(runce)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* [[ENC:@[0-9]+]], i64 0, i64 0), i8* bitcast (void (i8*, i8*)* @_TToFC14objc_protocols3Foo5runcefT_T_ to i8*) },
// CHECK: { i8*, i8*, i8* } { i8* getelementptr inbounds ([6 x i8], [6 x i8]* @"\01L_selector_data(funge)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* [[ENC]], i64 0, i64 0), i8* bitcast (void (i8*, i8*)* @_TToFC14objc_protocols3Foo5fungefT_T_ to i8*) },
// CHECK: { i8*, i8*, i8* } { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"\01L_selector_data(foo)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* [[ENC]], i64 0, i64 0), i8* bitcast (void (i8*, i8*)* @_TToFC14objc_protocols3Foo3foofT_T_ to i8*)
// CHECK: }]
// CHECK: }, section "__DATA, __objc_const", align 8
class Bar {
func bar() {}
}
// -- Bar does not directly have objc methods...
// CHECK-NOT: @_INSTANCE_METHODS_Bar
extension Bar : NSRuncing, NSFunging {
@objc func runce() {}
@objc func funge() {}
@objc func foo() {}
func notObjC() {}
}
// -- ...but the ObjC protocol conformances on its extension add some
// CHECK: @"_CATEGORY_INSTANCE_METHODS__TtC14objc_protocols3Bar_$_objc_protocols" = private constant { i32, i32, [3 x { i8*, i8*, i8* }] } {
// CHECK: i32 24, i32 3,
// CHECK: [3 x { i8*, i8*, i8* }] [
// CHECK: { i8*, i8*, i8* } { i8* getelementptr inbounds ([6 x i8], [6 x i8]* @"\01L_selector_data(runce)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* [[ENC]], i64 0, i64 0), i8* bitcast (void (i8*, i8*)* @_TToFC14objc_protocols3Bar5runcefT_T_ to i8*) },
// CHECK: { i8*, i8*, i8* } { i8* getelementptr inbounds ([6 x i8], [6 x i8]* @"\01L_selector_data(funge)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* [[ENC]], i64 0, i64 0), i8* bitcast (void (i8*, i8*)* @_TToFC14objc_protocols3Bar5fungefT_T_ to i8*) },
// CHECK: { i8*, i8*, i8* } { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"\01L_selector_data(foo)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* [[ENC]], i64 0, i64 0), i8* bitcast (void (i8*, i8*)* @_TToFC14objc_protocols3Bar3foofT_T_ to i8*) }
// CHECK: ]
// CHECK: }, section "__DATA, __objc_const", align 8
// class Bas from objc_protocols_Bas module
extension Bas : NSRuncing {
// -- The runce() implementation comes from the original definition.
@objc public
func foo() {}
}
// CHECK: @"_CATEGORY_INSTANCE_METHODS__TtC18objc_protocols_Bas3Bas_$_objc_protocols" = private constant { i32, i32, [1 x { i8*, i8*, i8* }] } {
// CHECK: i32 24, i32 1,
// CHECK; [1 x { i8*, i8*, i8* }] [
// CHECK: { i8*, i8*, i8* } { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"\01L_selector_data(foo)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* [[ENC]], i64 0, i64 0), i8* bitcast (void (i8*, i8*)* @_TToFE14objc_protocolsC18objc_protocols_Bas3Bas3foofT_T_ to i8*) }
// CHECK: ]
// CHECK: }, section "__DATA, __objc_const", align 8
// -- Swift protocol refinement of ObjC protocols.
protocol Frungible : NSRuncing, NSFunging {
func frunge()
}
class Zim : Frungible {
@objc func runce() {}
@objc func funge() {}
@objc func foo() {}
func frunge() {}
}
// CHECK: @_INSTANCE_METHODS__TtC14objc_protocols3Zim = private constant { i32, i32, [3 x { i8*, i8*, i8* }] } {
// CHECK: i32 24, i32 3,
// CHECK: [3 x { i8*, i8*, i8* }] [
// CHECK: { i8*, i8*, i8* } { i8* getelementptr inbounds ([6 x i8], [6 x i8]* @"\01L_selector_data(runce)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* [[ENC]], i64 0, i64 0), i8* bitcast (void (i8*, i8*)* @_TToFC14objc_protocols3Zim5runcefT_T_ to i8*) },
// CHECK: { i8*, i8*, i8* } { i8* getelementptr inbounds ([6 x i8], [6 x i8]* @"\01L_selector_data(funge)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* [[ENC]], i64 0, i64 0), i8* bitcast (void (i8*, i8*)* @_TToFC14objc_protocols3Zim5fungefT_T_ to i8*) },
// CHECK: { i8*, i8*, i8* } { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"\01L_selector_data(foo)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* [[ENC]], i64 0, i64 0), i8* bitcast (void (i8*, i8*)* @_TToFC14objc_protocols3Zim3foofT_T_ to i8*) }
// CHECK: ]
// CHECK: }, section "__DATA, __objc_const", align 8
// class Zang from objc_protocols_Bas module
extension Zang : Frungible {
@objc public
func runce() {}
// funge() implementation from original definition of Zang
@objc public
func foo() {}
func frunge() {}
}
// CHECK: @"_CATEGORY_INSTANCE_METHODS__TtC18objc_protocols_Bas4Zang_$_objc_protocols" = private constant { i32, i32, [2 x { i8*, i8*, i8* }] } {
// CHECK: i32 24, i32 2,
// CHECK: [2 x { i8*, i8*, i8* }] [
// CHECK: { i8*, i8*, i8* } { i8* getelementptr inbounds ([6 x i8], [6 x i8]* @"\01L_selector_data(runce)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* [[ENC]], i64 0, i64 0), i8* bitcast (void (i8*, i8*)* @_TToFE14objc_protocolsC18objc_protocols_Bas4Zang5runcefT_T_ to i8*) },
// CHECK: { i8*, i8*, i8* } { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"\01L_selector_data(foo)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* [[ENC]], i64 0, i64 0), i8* bitcast (void (i8*, i8*)* @_TToFE14objc_protocolsC18objc_protocols_Bas4Zang3foofT_T_ to i8*) }
// CHECK: ]
// CHECK: }, section "__DATA, __objc_const", align 8
// -- Force generation of witness for Zim.
// CHECK: define hidden { %objc_object*, i8** } @_TF14objc_protocols22mixed_heritage_erasure{{.*}}
func mixed_heritage_erasure(x: Zim) -> Frungible {
return x
// CHECK: [[T0:%.*]] = insertvalue { %objc_object*, i8** } undef, %objc_object* {{%.*}}, 0
// CHECK: insertvalue { %objc_object*, i8** } [[T0]], i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* [[ZIM_FRUNGIBLE_WITNESS]], i32 0, i32 0), 1
}
// CHECK-LABEL: define hidden void @_TF14objc_protocols12objc_generic{{.*}}(%objc_object*, %swift.type* %T) {{.*}} {
func objc_generic<T : NSRuncing>(x: T) {
x.runce()
// CHECK: [[SELECTOR:%.*]] = load i8*, i8** @"\01L_selector(runce)", align 8
// CHECK: bitcast %objc_object* %0 to [[OBJTYPE:.*]]*
// CHECK: call void bitcast (void ()* @objc_msgSend to void ([[OBJTYPE]]*, i8*)*)([[OBJTYPE]]* {{%.*}}, i8* [[SELECTOR]])
}
// CHECK-LABEL: define hidden void @_TF14objc_protocols17call_objc_generic{{.*}}(%objc_object*, %swift.type* %T) {{.*}} {
// CHECK: call void @_TF14objc_protocols12objc_generic{{.*}}(%objc_object* %0, %swift.type* %T)
func call_objc_generic<T : NSRuncing>(x: T) {
objc_generic(x)
}
// CHECK-LABEL: define hidden void @_TF14objc_protocols13objc_protocol{{.*}}(%objc_object*) {{.*}} {
func objc_protocol(x: NSRuncing) {
x.runce()
// CHECK: [[SELECTOR:%.*]] = load i8*, i8** @"\01L_selector(runce)", align 8
// CHECK: bitcast %objc_object* %0 to [[OBJTYPE:.*]]*
// CHECK: call void bitcast (void ()* @objc_msgSend to void ([[OBJTYPE]]*, i8*)*)([[OBJTYPE]]* {{%.*}}, i8* [[SELECTOR]])
}
// CHECK: define hidden %objc_object* @_TF14objc_protocols12objc_erasure{{.*}}(%CSo7NSSpoon*) {{.*}} {
func objc_erasure(x: NSSpoon) -> NSRuncing {
return x
// CHECK: [[RES:%.*]] = bitcast %CSo7NSSpoon* {{%.*}} to %objc_object*
// CHECK: ret %objc_object* [[RES]]
}
// CHECK: define hidden void @_TF14objc_protocols25objc_protocol_composition{{.*}}(%objc_object*)
func objc_protocol_composition(x: protocol<NSRuncing, NSFunging>) {
x.runce()
// CHECK: [[RUNCE:%.*]] = load i8*, i8** @"\01L_selector(runce)", align 8
// CHECK: bitcast %objc_object* %0 to [[OBJTYPE:.*]]*
// CHECK: call void bitcast (void ()* @objc_msgSend to void ([[OBJTYPE]]*, i8*)*)([[OBJTYPE]]* {{%.*}}, i8* [[RUNCE]])
x.funge()
// CHECK: [[FUNGE:%.*]] = load i8*, i8** @"\01L_selector(funge)", align 8
// CHECK: call void bitcast (void ()* @objc_msgSend to void ([[OBJTYPE]]*, i8*)*)([[OBJTYPE]]* {{%.*}}, i8* [[FUNGE]])
}
// CHECK: define hidden void @_TF14objc_protocols31objc_swift_protocol_composition{{.*}}(%objc_object*, i8**)
func objc_swift_protocol_composition
(x:protocol<NSRuncing, Ansible, NSFunging>) {
x.runce()
// CHECK: [[RUNCE:%.*]] = load i8*, i8** @"\01L_selector(runce)", align 8
// CHECK: bitcast %objc_object* %0 to [[OBJTYPE:.*]]*
// CHECK: call void bitcast (void ()* @objc_msgSend to void ([[OBJTYPE]]*, i8*)*)([[OBJTYPE]]* {{%.*}}, i8* [[RUNCE]])
/* TODO: Abstraction difference from ObjC protocol composition to
* opaque protocol
x.anse()
*/
x.funge()
// CHECK: [[FUNGE:%.*]] = load i8*, i8** @"\01L_selector(funge)", align 8
// CHECK: call void bitcast (void ()* @objc_msgSend to void ([[OBJTYPE]]*, i8*)*)([[OBJTYPE]]* {{%.*}}, i8* [[FUNGE]])
}
// TODO: Mixed class-bounded/fully general protocol compositions.
@objc protocol SettableProperty {
var reqt: NSRuncing { get set }
}
func instantiateArchetype<T: SettableProperty>(x: T) {
let y = x.reqt
x.reqt = y
}
// rdar://problem/21029254
@objc protocol Appaloosa { }
protocol Palomino {}
protocol Vanner : Palomino, Appaloosa { }
struct Stirrup<T : Palomino> { }
func canter<T : Palomino>(t: Stirrup<T>) {}
func gallop<T : Vanner>(t: Stirrup<T>) {
canter(t)
}