Skip to content

Sema: Use capture list to represent dynamic metatype bases for partial applications. #61374

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 108 additions & 25 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1186,10 +1186,30 @@ namespace {
thunk->setParameterList(thunkParamList);
thunk->setThunkKind(AutoClosureExpr::Kind::SingleCurryThunk);
cs.cacheType(thunk);

// If the `self` type is existential, it must be opened.
OpaqueValueExpr *baseOpened = nullptr;
Expr *origBaseExpr = baseExpr;
if (baseExpr) {
auto baseTy = cs.getType(baseExpr);
if (baseTy->isAnyExistentialType()) {
Type openedTy = solution.OpenedExistentialTypes.lookup(
cs.getConstraintLocator(locator));
assert(openedTy);

Type opaqueValueTy = openedTy;
if (baseTy->is<ExistentialMetatypeType>())
opaqueValueTy = MetatypeType::get(opaqueValueTy);

baseOpened = new (ctx) OpaqueValueExpr(SourceLoc(), opaqueValueTy);
cs.cacheType(baseOpened);
baseExpr = baseOpened;
}
}

Expr *thunkBody = buildSingleCurryThunkBodyCall(
baseExpr, fnExpr, declOrClosure, thunkParamList, locator);

// If we called a function with a dynamic 'Self' result, we may need some
// special handling.
if (baseExpr) {
Expand Down Expand Up @@ -1219,6 +1239,15 @@ namespace {
// Now, coerce to the result type of the thunk.
thunkBody = coerceToType(thunkBody, thunkTy->getResult(), locator);

// Close up the existential if necessary.
if (baseOpened) {
thunkBody = new (ctx) OpenExistentialExpr(origBaseExpr,
baseOpened,
thunkBody,
thunkBody->getType());
cs.cacheType(thunkBody);
}

if (thunkTy->getExtInfo().isThrowing()) {
thunkBody = new (ctx)
TryExpr(thunkBody->getStartLoc(), thunkBody, cs.getType(thunkBody),
Expand Down Expand Up @@ -1529,13 +1558,14 @@ namespace {
// inside the curry thunk as well. This reduces abstraction and
// post-factum function type conversions, and results in better SILGen.
//
// For a partial application of a class method, however, we always want
// the thunk to accept a class to avoid potential abstraction, so the
// existential base must be opened eagerly in order to be upcast to the
// appropriate class reference type before it is passed to the thunk.
// For a partial application of a class instance method, however, we
// always want the thunk to accept a class to avoid potential
// abstraction, so the existential base must be opened eagerly in order
// to be upcast to the appropriate class reference type before it is
// passed to the thunk.
if (!needsCurryThunk ||
(!member->getDeclContext()->getSelfProtocolDecl() &&
!isUnboundInstanceMember)) {
baseIsInstance && member->isInstanceMember())) {
// Open the existential before performing the member reference.
base = openExistentialReference(base, knownOpened->second, member);
baseTy = knownOpened->second;
Expand Down Expand Up @@ -1745,26 +1775,79 @@ namespace {
memberLocator);
} else if (needsCurryThunk) {
// Another case where we want to build a single closure is when
// we have a partial application of a static member on a statically-
// derived metatype value. Again, there are no order of evaluation
// concerns here, and keeping the call and base together in the AST
// improves SILGen.
if ((isa<ConstructorDecl>(member)
|| member->isStatic()) &&
cs.isStaticallyDerivedMetatype(base)) {
// Add a useless ".self" to avoid downstream diagnostics.
base = new (context) DotSelfExpr(base, SourceLoc(), base->getEndLoc(),
cs.getType(base));
cs.setType(base, base->getType());

auto *closure = buildSingleCurryThunk(
base, declRefExpr, cast<AbstractFunctionDecl>(member),
// we have a partial application of a static member. It is better
// to either push the base reference down into the closure (if it's
// just a literal type reference, in which case there are no order of
// operation concerns with capturing vs. evaluating it in the closure),
// or to evaluate the base as a capture and hand it down via the
// capture list.
if (isa<ConstructorDecl>(member) || member->isStatic()) {
if (cs.isStaticallyDerivedMetatype(base)) {
// Add a useless ".self" to avoid downstream diagnostics.
base = new (context) DotSelfExpr(base, SourceLoc(), base->getEndLoc(),
cs.getType(base));
cs.setType(base, base->getType());

auto *closure = buildSingleCurryThunk(
base, declRefExpr, cast<AbstractFunctionDecl>(member),
memberLocator);

// Skip the code below -- we're not building an extra level of
// call by applying the metatype; instead, the closure we just
// built is the curried reference.
return closure;
} else {
// Add a useless ".self" to avoid downstream diagnostics, in case
// the type ref is still a TypeExpr.
base = new (context) DotSelfExpr(base, SourceLoc(), base->getEndLoc(),
cs.getType(base));
// Introduce a capture variable.
cs.cacheType(base);
solution.setExprTypes(base);
auto capture = new (context) VarDecl(/*static*/ false,
VarDecl::Introducer::Let,
SourceLoc(),
context.getIdentifier("$base$"),
dc);
capture->setImplicit();
capture->setInterfaceType(base->getType()->mapTypeOutOfContext());

NamedPattern *capturePat = new (context) NamedPattern(capture);
capturePat->setImplicit();
capturePat->setType(base->getType());

auto capturePBE = PatternBindingEntry(capturePat,
SourceLoc(), base, dc);
auto captureDecl = PatternBindingDecl::create(context, SourceLoc(),
{}, SourceLoc(),
capturePBE,
dc);
captureDecl->setImplicit();

// Write the closure in terms of the capture.
auto baseRef = new (context)
DeclRefExpr(capture, DeclNameLoc(base->getLoc()), /*implicit*/ true);
baseRef->setType(base->getType());
cs.cacheType(baseRef);

auto *closure = buildSingleCurryThunk(
baseRef, declRefExpr, cast<AbstractFunctionDecl>(member),
simplifyType(adjustedOpenedType)->castTo<FunctionType>(),
memberLocator);

// Skip the code below -- we're not building an extra level of
// call by applying the metatype; instead, the closure we just
// built is the curried reference.
return closure;

// Wrap the closure in a capture list.
auto captureEntry = CaptureListEntry(captureDecl);
auto captureExpr = CaptureListExpr::create(context, captureEntry,
closure);
captureExpr->setImplicit();
captureExpr->setType(cs.getType(closure));
cs.cacheType(captureExpr);

Expr *finalExpr = captureExpr;
closeExistentials(finalExpr, locator,
/*force*/ openedExistential);
return finalExpr;
}
}

FunctionType *curryThunkTy = nullptr;
Expand Down
121 changes: 121 additions & 0 deletions test/SILGen/closure_literal_reabstraction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@ struct Butt {
static prefix func !=< (a: Butt) -> Butt { return a }
}

class AbstractButt {
required init(x: Int) {}

class func create(x: Int) -> Self { return self.init(x: x) }
}

class AbstractGenericButt<T> {
required init(c: Int) {}
class func create(c: Int) -> Self { return self.init(c: c) }
}

func abstractButtFactory() -> AbstractButt.Type { return AbstractButt.self }

protocol Buttable {
init(p: Int)
static func create(p: Int) -> Self
}

// CHECK-LABEL: sil {{.*}} @{{.*}}reabstractCaptureListExprArgument
// CHECK: [[CLOSURE_FN:%.*]] = function_ref {{.*}}U_
// CHECK: [[CLOSURE:%.*]] = partial_apply {{.*}}[[CLOSURE_FN]]
Expand Down Expand Up @@ -65,7 +83,110 @@ func reabstractCoercedMemberOperatorRef() {
gen(f: (!=<) as (Butt) -> Butt)
}

// CHECK-LABEL: sil {{.*}} @{{.*}}reabstractDynamicMetatypeInitializerRef
// CHECK: [[CLOSURE_FN:%.*]] = function_ref {{.*}}u_
// CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_FN]]
// CHECK: [[CLOSURE_NE:%.*]] = convert_escape_to_noescape [not_guaranteed] [[CLOSURE]]
// CHECK: apply {{.*}}<Int, AbstractButt>([[CLOSURE_NE]])
func reabstractDynamicMetatypeInitializerRef() {
gen(f: abstractButtFactory().init)
}

// CHECK-LABEL: sil {{.*}} @{{.*}}reabstractDynamicMetatypeStaticMemberRef
// CHECK: [[CLOSURE_FN:%.*]] = function_ref {{.*}}u_
// CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_FN]]
// CHECK: [[CLOSURE_NE:%.*]] = convert_escape_to_noescape [not_guaranteed] [[CLOSURE]]
// CHECK: apply {{.*}}<Int, AbstractButt>([[CLOSURE_NE]])
func reabstractDynamicMetatypeStaticMemberRef() {
gen(f: abstractButtFactory().create)
}

// CHECK-LABEL: sil {{.*}} @{{.*}}reabstractGenericInitializerRef
// CHECK: [[CLOSURE_FN:%.*]] = function_ref {{.*}}u_
// CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_FN]]
// CHECK: [[CLOSURE_C:%.*]] = convert_function [[CLOSURE]]
// CHECK: [[CLOSURE_NE:%.*]] = convert_escape_to_noescape [not_guaranteed] [[CLOSURE_C]]
// CHECK: apply {{.*}}<Int, T>([[CLOSURE_NE]])
func reabstractGenericInitializerRef<T: Buttable>(butt: T) {
gen(f: T.init)
}

// CHECK-LABEL: sil {{.*}} @{{.*}}reabstractGenericStaticMemberRef
// CHECK: [[CLOSURE_FN:%.*]] = function_ref {{.*}}u_
// CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_FN]]
// CHECK: [[CLOSURE_C:%.*]] = convert_function [[CLOSURE]]
// CHECK: [[CLOSURE_NE:%.*]] = convert_escape_to_noescape [not_guaranteed] [[CLOSURE_C]]
// CHECK: apply {{.*}}<Int, T>([[CLOSURE_NE]])
func reabstractGenericStaticMemberRef<T: Buttable>(butt: T) {
gen(f: T.create)
}

// CHECK-LABEL: sil {{.*}} @{{.*}}reabstractDynamicGenericStaticMemberRef
// CHECK: [[CLOSURE_FN:%.*]] = function_ref {{.*}}u_
// CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_FN]]<T>(%0)
// CHECK: [[CLOSURE_C:%.*]] = convert_function [[CLOSURE]]
// CHECK: [[CLOSURE_NE:%.*]] = convert_escape_to_noescape [not_guaranteed] [[CLOSURE_C]]
// CHECK: apply {{.*}}<Int, T>([[CLOSURE_NE]])
func reabstractDynamicGenericStaticMemberRef<T: Buttable>(t: T.Type) {
gen(f: t.create)
}

// CHECK-LABEL: sil {{.*}} @{{.*}}reabstractExistentialInitializerRef
// CHECK: [[CLOSURE_FN:%.*]] = function_ref {{.*}}u_
// CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_FN]](%0)
// CHECK: [[CLOSURE_NE:%.*]] = convert_escape_to_noescape [not_guaranteed] [[CLOSURE]]
// CHECK: apply {{.*}}<Int, any Buttable>([[CLOSURE_NE]])
func reabstractExistentialInitializerRef(butt: any Buttable.Type) {
gen(f: butt.init)
}

// CHECK-LABEL: sil {{.*}} @{{.*}}reabstractExistentialStaticMemberRef
// CHECK: [[CLOSURE_FN:%.*]] = function_ref {{.*}}u_
// CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_FN]](%0)
// CHECK: [[CLOSURE_NE:%.*]] = convert_escape_to_noescape [not_guaranteed] [[CLOSURE]]
// CHECK: apply {{.*}}<Int, any Buttable>([[CLOSURE_NE]])
func reabstractExistentialStaticMemberRef(butt: any Buttable.Type) {
gen(f: butt.create)
}

// CHECK-LABEL: sil {{.*}} @{{.*}}reabstractClassCompInitializerRefs
func reabstractClassCompInitializerRefs<T>(
butt: any (AbstractGenericButt<T> & Buttable).Type
) {
// CHECK: [[CLOSURE_FN:%.*]] = function_ref {{.*}}u_
// CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_FN]]<T>(%0)
// CHECK: [[CLOSURE_C:%.*]] = convert_function [[CLOSURE]]
// CHECK: [[CLOSURE_NE:%.*]] = convert_escape_to_noescape [not_guaranteed] [[CLOSURE_C]]
// CHECK: apply {{.*}}<Int, any (AbstractGenericButt<T> & Buttable)>([[CLOSURE_NE]])
gen(f: butt.init(c:))
// CHECK: [[CLOSURE_FN:%.*]] = function_ref {{.*}}u0_
// CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_FN]]<T>(%0)
// CHECK: [[CLOSURE_C:%.*]] = convert_function [[CLOSURE]]
// CHECK: [[CLOSURE_NE:%.*]] = convert_escape_to_noescape [not_guaranteed] [[CLOSURE_C]]
// CHECK: apply {{.*}}<Int, any (AbstractGenericButt<T> & Buttable)>([[CLOSURE_NE]])
gen(f: butt.init(p:))
}

// CHECK-LABEL: sil {{.*}} @{{.*}}reabstractClassCompStaticMemberRefs
func reabstractClassCompStaticMemberRefs<T>(
butt: any (AbstractGenericButt<T> & Buttable).Type
) {
// CHECK: [[CLOSURE_FN:%.*]] = function_ref {{.*}}u_
// CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_FN]]<T>(%0)
// CHECK: [[CLOSURE_C:%.*]] = convert_function [[CLOSURE]]
// CHECK: [[CLOSURE_NE:%.*]] = convert_escape_to_noescape [not_guaranteed] [[CLOSURE_C]]
// CHECK: apply {{.*}}<Int, any (AbstractGenericButt<T> & Buttable)>([[CLOSURE_NE]])
gen(f: butt.create(c:))
// CHECK: [[CLOSURE_FN:%.*]] = function_ref {{.*}}u0_
// CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_FN]]<T>(%0)
// CHECK: [[CLOSURE_C:%.*]] = convert_function [[CLOSURE]]
// CHECK: [[CLOSURE_NE:%.*]] = convert_escape_to_noescape [not_guaranteed] [[CLOSURE_C]]
// CHECK: apply {{.*}}<Int, any (AbstractGenericButt<T> & Buttable)>([[CLOSURE_NE]])
gen(f: butt.create(p:))
}

// TODO

func reabstractInstanceMethodRef(instance: Butt) {
gen(f: instance.getX)
}
12 changes: 3 additions & 9 deletions test/SILGen/objc_protocols.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@

// RUN: %target-swift-emit-silgen -module-name objc_protocols -sdk %S/Inputs -I %S/Inputs -enable-source-import %s -disable-objc-attr-requires-foundation-module | %FileCheck %s

// REQUIRES: objc_interop

import gizmo
Expand Down Expand Up @@ -50,7 +48,7 @@ func objc_generic_partial_apply<T : NSRuncing>(_ x: T) {
// CHECK: [[FN:%.*]] = function_ref @$s14objc_protocols0A22_generic_partial_applyyyxAA9NSRuncingRzlFSo8NSObjectCycxcfu1_
_ = T.runce

// CHECK: [[FN:%.*]] = function_ref @$s14objc_protocols0A22_generic_partial_applyyyxAA9NSRuncingRzlFSo8NSObjectCycxmcfu3_
// CHECK: [[FN:%.*]] = function_ref @$s14objc_protocols0A22_generic_partial_applyyyxAA9NSRuncingRzlFSo8NSObjectCycfu3_
_ = T.mince
// CHECK-NOT: destroy_value [[ARG]]
}
Expand All @@ -75,13 +73,9 @@ func objc_generic_partial_apply<T : NSRuncing>(_ x: T) {
// CHECK: } // end sil function '$s14objc_protocols0A22_generic_partial_applyyyxAA9NSRuncingRzlFSo8NSObjectCycxcfu1_AEycfu2_'


// CHECK-LABEL: sil private [ossa] @$s14objc_protocols0A22_generic_partial_applyyyxAA9NSRuncingRzlFSo8NSObjectCycxmcfu3_ : $@convention(thin) <T where T : NSRuncing> (@thick T.Type) -> @owned @callee_guaranteed () -> @owned NSObject
// CHECK: function_ref @$s14objc_protocols0A22_generic_partial_applyyyxAA9NSRuncingRzlFSo8NSObjectCycxmcfu3_AEycfu4_ : $@convention(thin) <τ_0_0 where τ_0_0 : NSRuncing> (@thick τ_0_0.Type) -> @owned NSObject
// CHECK: } // end sil function '$s14objc_protocols0A22_generic_partial_applyyyxAA9NSRuncingRzlFSo8NSObjectCycxmcfu3_'

// CHECK-LABEL: sil private [ossa] @$s14objc_protocols0A22_generic_partial_applyyyxAA9NSRuncingRzlFSo8NSObjectCycxmcfu3_AEycfu4_ : $@convention(thin) <T where T : NSRuncing> (@thick T.Type) -> @owned NSObject
// CHECK-LABEL: sil private [ossa] @$s14objc_protocols0A22_generic_partial_applyyyxAA9NSRuncingRzlFSo8NSObjectCycfu3_ : $@convention(thin) <T where T : NSRuncing> (@thick T.Type) -> @owned NSObject
// CHECK: objc_method %2 : $@objc_metatype T.Type, #NSRuncing.mince!foreign : <Self where Self : NSRuncing> (Self.Type) -> () -> NSObject, $@convention(objc_method) <τ_0_0 where τ_0_0 : NSRuncing> (@objc_metatype τ_0_0.Type) -> @autoreleased NSObject
// CHECK: } // end sil function '$s14objc_protocols0A22_generic_partial_applyyyxAA9NSRuncingRzlFSo8NSObjectCycxmcfu3_AEycfu4_'
// CHECK: } // end sil function '$s14objc_protocols0A22_generic_partial_applyyyxAA9NSRuncingRzlFSo8NSObjectCycfu3_'


// CHECK-LABEL: sil hidden [ossa] @$s14objc_protocols0A9_protocol{{[_0-9a-zA-Z]*}}F
Expand Down
8 changes: 4 additions & 4 deletions test/SILGen/partial_apply_generic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ protocol Foo {

// CHECK-LABEL: sil hidden [ossa] @$s21partial_apply_generic14getStaticFunc1{{[_0-9a-zA-Z]*}}F
func getStaticFunc1<T: Foo>(t: T.Type) -> () -> () {
// CHECK: function_ref @$s21partial_apply_generic14getStaticFunc11tyycxm_tAA3FooRzlFyycxmcfu_ : $@convention(thin) <τ_0_0 where τ_0_0 : Foo> (@thick τ_0_0.Type) -> @owned @callee_guaranteed () -> ()
// CHECK: function_ref @$s21partial_apply_generic14getStaticFunc11tyycxm_tAA3FooRzlFyycfu_ : $@convention(thin) <τ_0_0 where τ_0_0 : Foo> (@thick τ_0_0.Type) -> ()
return t.staticFunc
}

// CHECK-LABEL: sil private [ossa] @$s21partial_apply_generic14getStaticFunc11tyycxm_tAA3FooRzlFyycxmcfu_yycfu0_ : $@convention(thin) <T where T : Foo> (@thick T.Type) -> ()
// CHECK-LABEL: sil private [ossa] @$s21partial_apply_generic14getStaticFunc11tyycxm_tAA3FooRzlFyycfu_ : $@convention(thin) <T where T : Foo> (@thick T.Type) -> ()
// CHECK: witness_method $T, #Foo.staticFunc :


// CHECK-LABEL: sil hidden [ossa] @$s21partial_apply_generic14getStaticFunc2{{[_0-9a-zA-Z]*}}F
func getStaticFunc2<T: Foo>(t: T) -> () -> () {
// CHECK: function_ref @$s21partial_apply_generic14getStaticFunc21tyycx_tAA3FooRzlFyycxmcfu_ : $@convention(thin) <τ_0_0 where τ_0_0 : Foo> (@thick τ_0_0.Type) -> @owned @callee_guaranteed () -> ()
// CHECK: function_ref @$s21partial_apply_generic14getStaticFunc21tyycx_tAA3FooRzlFyycfu_ : $@convention(thin) <τ_0_0 where τ_0_0 : Foo> (@thick τ_0_0.Type) -> ()
return T.staticFunc
}

// CHECK-LABEL: sil private [ossa] @$s21partial_apply_generic14getStaticFunc21tyycx_tAA3FooRzlFyycxmcfu_yycfu0_ : $@convention(thin) <T where T : Foo> (@thick T.Type) -> ()
// CHECK-LABEL: sil private [ossa] @$s21partial_apply_generic14getStaticFunc21tyycx_tAA3FooRzlFyycfu_ : $@convention(thin) <T where T : Foo> (@thick T.Type) -> ()
// CHECK: witness_method $T, #Foo.staticFunc :


Expand Down
Loading