Skip to content

[borrow-operator] Add initial support for applying _borrow to self when calling a method. #62855

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
14 changes: 13 additions & 1 deletion lib/SILGen/SILGenApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2973,7 +2973,19 @@ ArgumentSource::findStorageReferenceExprForBorrowExpr(SILGenFunction &SGF) && {
if (!isExpr())
return nullptr;

auto argExpr = asKnownExpr();
// We support two patterns:
//
// (load_expr (borrow_expr))
// *or*
// (paren_expr (load_expr (borrow_expr)))
//
// The first happens if a borrow is used on a non-self argument. The second
// happens if we pass self as a borrow.
auto *argExpr = asKnownExpr();

if (auto *parenExpr = dyn_cast<ParenExpr>(argExpr))
argExpr = parenExpr->getSubExpr();

auto *li = dyn_cast<LoadExpr>(argExpr);
if (!li)
return nullptr;
Expand Down
265 changes: 260 additions & 5 deletions test/SILGen/borrow_expr.swift
Original file line number Diff line number Diff line change
@@ -1,16 +1,58 @@
// RUN: %target-swift-frontend -enable-experimental-move-only -o - -emit-silgen %s | %FileCheck %s

class Klass {
final class Klass {
func useKlass() {}

func doSomething() {}
func doSomething(_ k: Klass) {}
}

func useKlass(_ k: Klass) {}

struct Struct {
var k = Klass()

func doSomething() {}
func doSomething(_ k: Klass) {}

var computedK: Klass { Klass() }
}

func useKlass(_ k: Klass) {}
func useStruct(_ s: Struct) {}

/////////////////////////
// Concrete Type Tests //
/////////////////////////

// CHECK-LABEL: sil hidden [ossa] @$s11borrow_expr10simpleTestyyF : $@convention(thin) () -> () {
// CHECK-LABEL: sil hidden [ossa] @$s11borrow_expr13simpleTestArgyyF : $@convention(thin) () -> () {
// CHECK: [[ADDR:%.*]] = project_box
//
// First check without the borrow:
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
// CHECK: [[VAL:%.*]] = load [copy] [[ACCESS]]
// CHECK: end_access [[ACCESS]]
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr9useStructyyAA0D0VF : $@convention(thin) (@guaranteed Struct) -> ()
// CHECK: apply [[FUNC]]([[VAL]])
// CHECK: destroy_value [[VAL]]
//
// Now with the borrow:
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
// CHECK: [[VAL:%.*]] = load_borrow [[ACCESS]]
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr9useStructyyAA0D0VF : $@convention(thin) (@guaranteed Struct) -> ()
// CHECK: apply [[FUNC]]([[VAL]])
// CHECK: end_borrow [[VAL]]
// CHECK: end_access [[ACCESS]]
// CHECK: } // end sil function '$s11borrow_expr13simpleTestArgyyF'
func simpleTestArg() {
var s = Struct()
s = Struct()
// Without borrow.
useStruct(s)
// With borrow.
useStruct(_borrow s)
}

// CHECK-LABEL: sil hidden [ossa] @$s11borrow_expr18simpleTestArgFieldyyF : $@convention(thin) () -> () {
// CHECK: [[ADDR:%.*]] = project_box
//
// First check without the borrow:
Expand All @@ -30,12 +72,225 @@ func useKlass(_ k: Klass) {}
// CHECK: apply [[FUNC]]([[VAL]])
// CHECK: end_borrow [[VAL]]
// CHECK: end_access [[ACCESS]]
// CHECK: } // end sil function '$s11borrow_expr10simpleTestyyF'
func simpleTest() {
// CHECK: } // end sil function '$s11borrow_expr18simpleTestArgFieldyyF'
func simpleTestArgField() {
var s = Struct()
s = Struct()
// Without borrow.
useKlass(s.k)
// With borrow.
useKlass(_borrow s.k)
}

// CHECK-LABEL: sil hidden [ossa] @$s11borrow_expr14simpleTestSelfyyF : $@convention(thin) () -> () {
// CHECK: [[ADDR:%.*]] = project_box
//
// First check without the borrow:
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
// CHECK: [[VAL:%.*]] = load [copy] [[ACCESS]]
// CHECK: end_access [[ACCESS]]
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr6StructV11doSomethingyyF : $@convention(method) (@guaranteed Struct) -> ()
// CHECK: apply [[FUNC]]([[VAL]])
// CHECK: destroy_value [[VAL]]
//
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
// CHECK: [[VAL:%.*]] = load [copy] [[ACCESS]]
// CHECK: end_access [[ACCESS]]
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr6StructV11doSomethingyyAA5KlassCF : $@convention(method) (@guaranteed Klass, @guaranteed Struct) -> ()
// CHECK: apply [[FUNC]]({{%.*}}, [[VAL]])
// CHECK: destroy_value [[VAL]]
//
// Now with the borrow:
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
// CHECK: [[VAL:%.*]] = load_borrow [[ACCESS]]
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr6StructV11doSomethingyyF : $@convention(method) (@guaranteed Struct) -> ()
// CHECK: apply [[FUNC]]([[VAL]])
// CHECK: end_borrow [[VAL]]
// CHECK: end_access [[ACCESS]]
//
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
// CHECK: [[VAL:%.*]] = load_borrow [[ACCESS]]
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr6StructV11doSomethingyyAA5KlassCF : $@convention(method) (@guaranteed Klass, @guaranteed Struct) -> ()
// CHECK: apply [[FUNC]]({{%.*}}, [[VAL]])
// CHECK: end_borrow [[VAL]]
// CHECK: end_access [[ACCESS]]
//
// CHECK: } // end sil function '$s11borrow_expr14simpleTestSelfyyF'
func simpleTestSelf() {
var s = Struct()
s = Struct()
// Without borrow.
s.doSomething()
s.doSomething(Klass())

// With borrow.
(_borrow s).doSomething()
(_borrow s).doSomething(Klass())
}

// CHECK-LABEL: sil hidden [ossa] @$s11borrow_expr19simpleTestSelfFieldyyF : $@convention(thin) () -> () {
// CHECK: [[ADDR:%.*]] = project_box
//
// First check without the borrow:
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
// CHECK: [[GEP:%.*]] = struct_element_addr [[ACCESS]] : $*Struct, #Struct.k
// CHECK: [[VAL:%.*]] = load [copy] [[GEP]]
// CHECK: end_access [[ACCESS]]
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr5KlassC11doSomethingyyF : $@convention(method) (@guaranteed Klass) -> ()
// CHECK: apply [[FUNC]]([[VAL]])
// CHECK: destroy_value [[VAL]]
//
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
// CHECK: [[GEP:%.*]] = struct_element_addr [[ACCESS]] : $*Struct, #Struct.k
// CHECK: [[VAL:%.*]] = load [copy] [[GEP]]
// CHECK: end_access [[ACCESS]]
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr5KlassC11doSomethingyyACF : $@convention(method) (@guaranteed Klass, @guaranteed Klass) -> ()
// CHECK: apply [[FUNC]]({{%.*}}, [[VAL]])
// CHECK: destroy_value [[VAL]]
//
// Now with the borrow:
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
// CHECK: [[GEP:%.*]] = struct_element_addr [[ACCESS]] : $*Struct, #Struct.k
// CHECK: [[VAL:%.*]] = load_borrow [[GEP]]
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr5KlassC11doSomethingyyF : $@convention(method) (@guaranteed Klass) -> (
// CHECK: apply [[FUNC]]([[VAL]])
// CHECK: end_borrow [[VAL]]
// CHECK: end_access [[ACCESS]]
//
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
// CHECK: [[GEP:%.*]] = struct_element_addr [[ACCESS]] : $*Struct, #Struct.k
// CHECK: [[VAL:%.*]] = load_borrow [[GEP]]
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr5KlassC11doSomethingyyACF : $@convention(method) (@guaranteed Klass, @guaranteed Klass) -> ()
// CHECK: apply [[FUNC]]({{%.*}}, [[VAL]])
// CHECK: end_borrow [[VAL]]
// CHECK: end_access [[ACCESS]]
//
// CHECK: } // end sil function '$s11borrow_expr19simpleTestSelfFieldyyF'
func simpleTestSelfField() {
var s = Struct()
s = Struct()
// Without borrow.
s.k.doSomething()
s.k.doSomething(Klass())

// With borrow.
(_borrow s.k).doSomething()
(_borrow s.k).doSomething(Klass())
}

////////////////////////
// Address Only Tests //
////////////////////////

protocol Q {
}

protocol P {
var q: Q { get /*_read*/ }
func doSomething()
func doSomething(_ k: Klass)
}

func usePExistential(_ p: P) {}
func usePGeneric<T : P>(_ p: T) {}
func useQExistential<T : Q>(_ q: T) {}
func useQGeneric<T : Q>(_ q: T) {}

//--------------------------------------------------------------------------------
// Generics
//

// CHECK-LABEL: sil hidden [ossa] @$s11borrow_expr20simpleTestGenericArgyyxAA1PRzlF : $@convention(thin) <T where T : P> (@in_guaranteed T) -> () {
// CHECK: [[ADDR:%.*]] = project_box
//
// First without borrow.
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
// CHECK: [[STACK:%.*]] = alloc_stack $T
// CHECK: copy_addr [[ACCESS]] to [init] [[STACK]]
// CHECK: end_access [[ACCESS]]
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr11usePGenericyyxAA1PRzlF : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
// CHECK: apply [[FUNC]]<T>([[STACK]])
// CHECK: destroy_addr [[STACK]]
// CHECK: dealloc_stack [[STACK]]
//
// Now with borrow
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr11usePGenericyyxAA1PRzlF : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
// CHECK: apply [[FUNC]]<T>([[ACCESS]])
// CHECK: end_access [[ACCESS]]
// CHECK: } // end sil function '$s11borrow_expr20simpleTestGenericArgyyxAA1PRzlF'
func simpleTestGenericArg<T : P>(_ pArg: T) {
var p = pArg
p = pArg

// Without borrow.
usePGeneric(p)

// With borrow.
usePGeneric(_borrow p)
}

// CHECK-LABEL: sil hidden [ossa] @$s11borrow_expr25simpleTestGenericArgFieldyyxAA1PRzlF : $@convention(thin) <T where T : P> (@in_guaranteed T) -> () {
// CHECK: } // end sil function '$s11borrow_expr25simpleTestGenericArgFieldyyxAA1PRzlF'
func simpleTestGenericArgField<T : P>(_ pArg: T) {
var p = pArg
p = pArg

// Without borrow.
useQGeneric(p.q)

// With borrow.
//
// TODO: This doesn't work now. We should support this potentially for
// _read. But protocols seem to not support _read at this time.
// useQGeneric(_borrow p.q)
Comment on lines +244 to +246
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting.

}

//--------------------------------------------------------------------------------
// Exisentials
//

// CHECK-LABEL: sil hidden [ossa] @$s11borrow_expr24simpleTestExistentialArgyyAA1P_pF : $@convention(thin) (@in_guaranteed any P) -> () {
// CHECK: [[ADDR:%.*]] = project_box {{%.*}} : ${ var any P }, 0
//
// First without the borrow.
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
// CHECK: [[STACK:%.*]] = alloc_stack $any P
// CHECK: copy_addr [[ACCESS]] to [init] [[STACK]]
// CHECK: end_access [[ACCESS]]
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr15usePExistentialyyAA1P_pF : $@convention(thin) (@in_guaranteed any P) -> ()
// CHECK: apply [[FUNC]]([[STACK]])
// CHECK: destroy_addr [[STACK]]
// CHECK: dealloc_stack [[STACK]]
//
// Now with the borrow.
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr15usePExistentialyyAA1P_pF : $@convention(thin) (@in_guaranteed any P) -> ()
// CHECK: apply [[FUNC]]([[ACCESS]])
// CHECK: end_access [[ACCESS]]
// CHECK: } // end sil function '$s11borrow_expr24simpleTestExistentialArgyyAA1P_pF'
func simpleTestExistentialArg(_ pArg: P) {
var p = pArg
p = pArg

// Without borrow.
usePExistential(p)

// With borrow.
usePExistential(_borrow p)
}


func simpleTestExistentialArgField(_ pArg: P) {
var p = pArg
p = pArg

// Without borrow.
useQGeneric(p.q)

// With borrow.
//
// TODO: This doesn't work now. We should support this potentially for
// _read. But protocols seem to not support _read at this time.
// useQGeneric(_borrow p.q)
}