Skip to content

Commit 94f1391

Browse files
committed
[borrow-expr] Wire up borrow expr to SILGenApply.
1 parent e8d2d02 commit 94f1391

File tree

6 files changed

+172
-10
lines changed

6 files changed

+172
-10
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6638,6 +6638,8 @@ ERROR(moveOnly_not_allowed_here,none,
66386638
"'moveOnly' may only be applied to classes, structs, and enums", ())
66396639
ERROR(move_expression_not_passed_lvalue,none,
66406640
"'move' can only be applied to lvalues", ())
6641+
ERROR(borrow_expression_not_passed_lvalue,none,
6642+
"'borrow' can only be applied to lvalues", ())
66416643

66426644
//------------------------------------------------------------------------------
66436645
// MARK: Type Wrappers

lib/SILGen/ArgumentSource.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ class ArgumentSource {
224224

225225
Expr *findStorageReferenceExprForBorrow() &&;
226226
Expr *findStorageReferenceExprForMoveOnlyBorrow(SILGenFunction &SGF) &&;
227+
Expr *findStorageReferenceExprForBorrowExpr(SILGenFunction &SGF) &&;
227228

228229
/// Given that this source is an expression, extract and clear
229230
/// that expression.

lib/SILGen/SILGenApply.cpp

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2968,6 +2968,29 @@ Expr *ArgumentSource::findStorageReferenceExprForMoveOnlyBorrow(
29682968
return lvExpr;
29692969
}
29702970

2971+
Expr *
2972+
ArgumentSource::findStorageReferenceExprForBorrowExpr(SILGenFunction &SGF) && {
2973+
if (!isExpr())
2974+
return nullptr;
2975+
2976+
auto argExpr = asKnownExpr();
2977+
auto *li = dyn_cast<LoadExpr>(argExpr);
2978+
if (!li)
2979+
return nullptr;
2980+
auto *borrowExpr = dyn_cast<BorrowExpr>(li->getSubExpr());
2981+
if (!borrowExpr)
2982+
return nullptr;
2983+
2984+
auto *lvExpr = ::findStorageReferenceExprForBorrow(borrowExpr->getSubExpr());
2985+
2986+
// Claim the value of this argument.
2987+
if (lvExpr) {
2988+
(void)std::move(*this).asKnownExpr();
2989+
}
2990+
2991+
return lvExpr;
2992+
}
2993+
29712994
Expr *ArgumentSource::findStorageReferenceExprForBorrow() && {
29722995
if (!isExpr()) return nullptr;
29732996

@@ -3097,18 +3120,23 @@ class ArgEmitter {
30973120
return;
30983121
}
30993122

3100-
// If this is a yield, and the yield is borrowed, emit a borrowed r-value.
3101-
if (IsYield && param.isGuaranteed()) {
3102-
if (tryEmitBorrowed(std::move(arg), loweredSubstArgType,
3103-
loweredSubstParamType, origParamType, paramSlice))
3123+
// If we have a guaranteed +0 parameter...
3124+
if (param.isGuaranteed()) {
3125+
// And this is a yield, emit a borrowed r-value.
3126+
if (IsYield) {
3127+
if (tryEmitBorrowed(std::move(arg), loweredSubstArgType,
3128+
loweredSubstParamType, origParamType, paramSlice))
3129+
return;
3130+
}
3131+
3132+
if (tryEmitBorrowExpr(std::move(arg), loweredSubstArgType,
3133+
loweredSubstParamType, origParamType, paramSlice))
31043134
return;
3105-
}
31063135

3107-
// If we have a guaranteed paramter, see if we have a move only type and can
3108-
// emit it borrow.
3109-
//
3110-
// We check for move only in tryEmitBorrowedMoveOnly.
3111-
if (param.isGuaranteed()) {
3136+
// If we have a guaranteed paramter, see if we have a move only type and
3137+
// can emit it borrow.
3138+
//
3139+
// We check for move only in tryEmitBorrowedMoveOnly.
31123140
if (tryEmitBorrowedMoveOnly(std::move(arg), loweredSubstArgType,
31133141
loweredSubstParamType, origParamType,
31143142
paramSlice))
@@ -3302,6 +3330,23 @@ class ArgEmitter {
33023330
return true;
33033331
}
33043332

3333+
bool tryEmitBorrowExpr(ArgumentSource &&arg, SILType loweredSubstArgType,
3334+
SILType loweredSubstParamType,
3335+
AbstractionPattern origParamType,
3336+
ClaimedParamsRef paramsSlice) {
3337+
assert(paramsSlice.size() == 1);
3338+
3339+
// Try to find an expression we can emit as a borrowed l-value.
3340+
auto lvExpr = std::move(arg).findStorageReferenceExprForBorrowExpr(SGF);
3341+
if (!lvExpr)
3342+
return false;
3343+
3344+
emitBorrowed(lvExpr, loweredSubstArgType, loweredSubstParamType,
3345+
origParamType, paramsSlice);
3346+
3347+
return true;
3348+
}
3349+
33053350
void emitBorrowed(Expr *arg, SILType loweredSubstArgType,
33063351
SILType loweredSubstParamType,
33073352
AbstractionPattern origParamType,

lib/Sema/MiscDiagnostics.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,11 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
344344
checkMoveExpr(moveExpr);
345345
}
346346

347+
// Diagnose move expression uses where the sub expression is not a declref expr
348+
if (auto *borrowExpr = dyn_cast<BorrowExpr>(E)) {
349+
checkBorrowExpr(borrowExpr);
350+
}
351+
347352
if (!HasReachedSemanticsProvidingExpr &&
348353
E == E->getSemanticsProvidingExpr()) {
349354
HasReachedSemanticsProvidingExpr = true;
@@ -408,6 +413,26 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
408413
}
409414
}
410415

416+
void checkBorrowExpr(BorrowExpr *borrowExpr) {
417+
// Make sure the MoveOnly feature is set. If not, error.
418+
// This should not currently be reached because the parse should ignore
419+
// the _move keyword unless the feature flag is set.
420+
if (!Ctx.LangOpts.hasFeature(Feature::MoveOnly)) {
421+
auto error =
422+
diag::experimental_moveonly_feature_can_only_be_used_when_enabled;
423+
Ctx.Diags.diagnose(borrowExpr->getLoc(), error);
424+
}
425+
426+
// Allow for a chain of member_ref exprs that end in a decl_ref expr.
427+
auto *subExpr = borrowExpr->getSubExpr();
428+
while (auto *memberRef = dyn_cast<MemberRefExpr>(subExpr))
429+
subExpr = memberRef->getBase();
430+
if (!isa<DeclRefExpr>(subExpr)) {
431+
Ctx.Diags.diagnose(borrowExpr->getLoc(),
432+
diag::borrow_expression_not_passed_lvalue);
433+
}
434+
}
435+
411436
static Expr *lookThroughArgument(Expr *arg) {
412437
while (1) {
413438
if (auto conv = dyn_cast<ImplicitConversionExpr>(arg))

test/SILGen/borrow_expr.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %target-swift-frontend -enable-experimental-move-only -o - -emit-silgen %s | %FileCheck %s
2+
3+
class Klass {
4+
func useKlass() {}
5+
}
6+
7+
struct Struct {
8+
var k = Klass()
9+
}
10+
11+
func useKlass(_ k: Klass) {}
12+
13+
// CHECK-LABEL: sil hidden [ossa] @$s11borrow_expr10simpleTestyyF : $@convention(thin) () -> () {
14+
// CHECK: [[ADDR:%.*]] = project_box
15+
//
16+
// First check without the borrow:
17+
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
18+
// CHECK: [[GEP:%.*]] = struct_element_addr [[ACCESS]] : $*Struct, #Struct.k
19+
// CHECK: [[VAL:%.*]] = load [copy] [[GEP]]
20+
// CHECK: end_access [[ACCESS]]
21+
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr8useKlassyyAA0D0CF : $@convention(thin) (@guaranteed Klass) -> ()
22+
// CHECK: apply [[FUNC]]([[VAL]])
23+
// CHECK: destroy_value [[VAL]]
24+
//
25+
// Now with the borrow:
26+
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[ADDR]]
27+
// CHECK: [[GEP:%.*]] = struct_element_addr [[ACCESS]] : $*Struct, #Struct.k
28+
// CHECK: [[VAL:%.*]] = load_borrow [[GEP]]
29+
// CHECK: [[FUNC:%.*]] = function_ref @$s11borrow_expr8useKlassyyAA0D0CF : $@convention(thin) (@guaranteed Klass) -> ()
30+
// CHECK: apply [[FUNC]]([[VAL]])
31+
// CHECK: end_borrow [[VAL]]
32+
// CHECK: end_access [[ACCESS]]
33+
// CHECK: } // end sil function '$s11borrow_expr10simpleTestyyF'
34+
func simpleTest() {
35+
var s = Struct()
36+
s = Struct()
37+
// Without borrow.
38+
useKlass(s.k)
39+
// With borrow.
40+
useKlass(_borrow s.k)
41+
}

test/Sema/borrow_expr.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,51 @@ func useApply() {
3737
s = S()
3838
(_borrow s).test()
3939
}
40+
41+
func testExprFailureLet() {
42+
let t = 5
43+
// Next line is parsed as move(t) + t
44+
let _ = _borrow t + t
45+
// Next line is parsed as move(t+t)
46+
let _ = _borrow (t+t) // expected-error {{'borrow' can only be applied to lvalues}}
47+
}
48+
49+
func testExprFailureVar() {
50+
var t = 5
51+
t = 5
52+
// Next line is parsed as move(t) + t
53+
let _ = _borrow t + t
54+
// Next line is parsed as move(t+t)
55+
let _ = _borrow (t+t) // expected-error {{'borrow' can only be applied to lvalues}}
56+
}
57+
58+
func letAddressOnly<T>(_ v: T) {
59+
let t = v
60+
let _ = _borrow t
61+
}
62+
63+
struct StructWithField {
64+
var k: Klass? = nil
65+
}
66+
67+
func testLetStructAccessField() {
68+
let t = StructWithField()
69+
let _ = _borrow t.k // expected-error {{'borrow' can only be applied to lvalues}}
70+
}
71+
72+
func testVarStructAccessField() {
73+
var t = StructWithField()
74+
t = StructWithField()
75+
let _ = _borrow t.k // expected-error {{'borrow' can only be applied to lvalues}}
76+
}
77+
78+
func testLetClassAccessField() {
79+
let t = Klass()
80+
let _ = _borrow t.k // expected-error {{'borrow' can only be applied to lvalues}}
81+
}
82+
83+
func testVarClassAccessField() {
84+
var t = Klass()
85+
t = Klass()
86+
let _ = _borrow t.k // expected-error {{'borrow' can only be applied to lvalues}}
87+
}

0 commit comments

Comments
 (0)