Skip to content

Commit b95fe48

Browse files
authored
Merge pull request #62742 from gottesmm/pr-947919228bebf49e02eae5aedb1efee8038c2b4b
[borrow-expr] Add simple support for borrow-expr and wire it up to SILGenApply
2 parents 961535d + 69c33fc commit b95fe48

File tree

14 files changed

+301
-19
lines changed

14 files changed

+301
-19
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,10 @@ ERROR(expected_expr_after_try, none,
13451345
"expected expression after 'try'", ())
13461346
ERROR(expected_expr_after_await, none,
13471347
"expected expression after 'await'", ())
1348+
ERROR(expected_expr_after_move, none,
1349+
"expected expression after '_move'", ())
1350+
ERROR(expected_expr_after_borrow, none,
1351+
"expected expression after '_borrow'", ())
13481352

13491353
// Cast expressions
13501354
ERROR(expected_type_after_is,none,

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6651,6 +6651,8 @@ ERROR(moveOnly_not_allowed_here,none,
66516651
"'moveOnly' may only be applied to classes, structs, and enums", ())
66526652
ERROR(move_expression_not_passed_lvalue,none,
66536653
"'move' can only be applied to lvalues", ())
6654+
ERROR(borrow_expression_not_passed_lvalue,none,
6655+
"'borrow' can only be applied to lvalues", ())
66546656

66556657
//------------------------------------------------------------------------------
66566658
// MARK: Type Wrappers

include/swift/AST/Expr.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2076,6 +2076,37 @@ class MoveExpr final : public IdentityExpr {
20762076
static bool classof(const Expr *e) { return e->getKind() == ExprKind::Move; }
20772077
};
20782078

2079+
/// BorrowExpr - A 'borrow' surrounding an lvalue/accessor expression at an
2080+
/// apply site marking the lvalue/accessor as being borrowed when passed to the
2081+
/// callee.
2082+
///
2083+
/// getSemanticsProvidingExpr() looks through this because it doesn't
2084+
/// provide the value and only very specific clients care where the
2085+
/// 'borrow' was written.
2086+
class BorrowExpr final : public IdentityExpr {
2087+
SourceLoc BorrowLoc;
2088+
2089+
public:
2090+
BorrowExpr(SourceLoc borrowLoc, Expr *sub, Type type = Type(),
2091+
bool implicit = false)
2092+
: IdentityExpr(ExprKind::Borrow, sub, type, implicit),
2093+
BorrowLoc(borrowLoc) {}
2094+
2095+
static BorrowExpr *createImplicit(ASTContext &ctx, SourceLoc borrowLoc,
2096+
Expr *sub, Type type = Type()) {
2097+
return new (ctx) BorrowExpr(borrowLoc, sub, type, /*implicit=*/true);
2098+
}
2099+
2100+
SourceLoc getLoc() const { return BorrowLoc; }
2101+
2102+
SourceLoc getStartLoc() const { return BorrowLoc; }
2103+
SourceLoc getEndLoc() const { return getSubExpr()->getEndLoc(); }
2104+
2105+
static bool classof(const Expr *e) {
2106+
return e->getKind() == ExprKind::Borrow;
2107+
}
2108+
};
2109+
20792110
/// TupleExpr - Parenthesized expressions like '(a: x+x)' and '(x, y, 4)'. Note
20802111
/// that expressions like '(4)' are represented with a ParenExpr.
20812112
class TupleExpr final : public Expr,

include/swift/AST/ExprNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ ABSTRACT_EXPR(Identity, Expr)
108108
EXPR(DotSelf, IdentityExpr)
109109
EXPR(Await, IdentityExpr)
110110
EXPR(Move, IdentityExpr)
111+
EXPR(Borrow, IdentityExpr)
111112
EXPR(UnresolvedMemberChainResult, IdentityExpr)
112113
EXPR_RANGE(Identity, Paren, UnresolvedMemberChainResult)
113114
ABSTRACT_EXPR(AnyTry, Expr)

lib/AST/ASTDumper.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,6 +2199,12 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
21992199
printRec(E->getSubExpr());
22002200
PrintWithColorRAII(OS, ParenthesisColor) << ')';
22012201
}
2202+
void visitBorrowExpr(BorrowExpr *E) {
2203+
printCommon(E, "borrow_expr");
2204+
OS << '\n';
2205+
printRec(E->getSubExpr());
2206+
PrintWithColorRAII(OS, ParenthesisColor) << ')';
2207+
}
22022208
void visitUnresolvedMemberChainResultExpr(UnresolvedMemberChainResultExpr *E){
22032209
printCommon(E, "unresolved_member_chain_expr");
22042210
OS << '\n';

lib/AST/ASTPrinter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4721,6 +4721,11 @@ void PrintAST::visitMoveExpr(MoveExpr *expr) {
47214721
visit(expr->getSubExpr());
47224722
}
47234723

4724+
void PrintAST::visitBorrowExpr(BorrowExpr *expr) {
4725+
Printer << "borrow ";
4726+
visit(expr->getSubExpr());
4727+
}
4728+
47244729
void PrintAST::visitInOutExpr(InOutExpr *expr) {
47254730
visit(expr->getSubExpr());
47264731
}

lib/AST/Expr.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ ConcreteDeclRef Expr::getReferencedDecl(bool stopAtParenExpr) const {
370370
PASS_THROUGH_REFERENCE(DotSelf, getSubExpr);
371371
PASS_THROUGH_REFERENCE(Await, getSubExpr);
372372
PASS_THROUGH_REFERENCE(Move, getSubExpr);
373+
PASS_THROUGH_REFERENCE(Borrow, getSubExpr);
373374
PASS_THROUGH_REFERENCE(Try, getSubExpr);
374375
PASS_THROUGH_REFERENCE(ForceTry, getSubExpr);
375376
PASS_THROUGH_REFERENCE(OptionalTry, getSubExpr);
@@ -729,6 +730,7 @@ bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const {
729730

730731
case ExprKind::Await:
731732
case ExprKind::Move:
733+
case ExprKind::Borrow:
732734
case ExprKind::Try:
733735
case ExprKind::ForceTry:
734736
case ExprKind::OptionalTry:
@@ -914,6 +916,7 @@ bool Expr::isValidParentOfTypeExpr(Expr *typeExpr) const {
914916
case ExprKind::Paren:
915917
case ExprKind::Await:
916918
case ExprKind::Move:
919+
case ExprKind::Borrow:
917920
case ExprKind::UnresolvedMemberChainResult:
918921
case ExprKind::Try:
919922
case ExprKind::ForceTry:

lib/Parse/ParseExpr.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ ParserResult<Expr> Parser::parseExprSequence(Diag<> Message,
370370
/// 'try' '?' expr-sequence-element(Mode)
371371
/// 'try' '!' expr-sequence-element(Mode)
372372
/// '_move' expr-sequence-element(Mode)
373+
/// 'borrow' expr-sequence-element(Mode)
373374
/// expr-unary(Mode)
374375
///
375376
/// 'try' is not actually allowed at an arbitrary position of a
@@ -405,17 +406,28 @@ ParserResult<Expr> Parser::parseExprSequenceElement(Diag<> message,
405406
return sub;
406407
}
407408

408-
if (Context.LangOpts.hasFeature(Feature::MoveOnly)
409-
&& Tok.isContextualKeyword("_move")) {
410-
Tok.setKind(tok::contextual_keyword);
411-
SourceLoc awaitLoc = consumeToken();
412-
ParserResult<Expr> sub =
413-
parseExprSequenceElement(diag::expected_expr_after_await, isExprBasic);
414-
if (!sub.hasCodeCompletion() && !sub.isNull()) {
415-
sub = makeParserResult(new (Context) MoveExpr(awaitLoc, sub.get()));
409+
if (Context.LangOpts.hasFeature(Feature::MoveOnly)) {
410+
if (Tok.isContextualKeyword("_move")) {
411+
Tok.setKind(tok::contextual_keyword);
412+
SourceLoc awaitLoc = consumeToken();
413+
ParserResult<Expr> sub =
414+
parseExprSequenceElement(diag::expected_expr_after_move, isExprBasic);
415+
if (!sub.hasCodeCompletion() && !sub.isNull()) {
416+
sub = makeParserResult(new (Context) MoveExpr(awaitLoc, sub.get()));
417+
}
418+
return sub;
416419
}
417420

418-
return sub;
421+
if (Tok.isContextualKeyword("_borrow")) {
422+
Tok.setKind(tok::contextual_keyword);
423+
SourceLoc awaitLoc = consumeToken();
424+
ParserResult<Expr> sub = parseExprSequenceElement(
425+
diag::expected_expr_after_borrow, isExprBasic);
426+
if (!sub.hasCodeCompletion() && !sub.isNull()) {
427+
sub = makeParserResult(new (Context) BorrowExpr(awaitLoc, sub.get()));
428+
}
429+
return sub;
430+
}
419431
}
420432

421433
SourceLoc tryLoc;

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
@@ -345,6 +345,11 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
345345
checkMoveExpr(moveExpr);
346346
}
347347

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

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

test/Parse/borrow_expr.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -enable-experimental-move-only
2+
3+
var global: Int = 5
4+
func testGlobal() {
5+
let _ = _borrow global
6+
}
7+
8+
func testLet() {
9+
let t = String()
10+
let _ = _borrow t
11+
}
12+
13+
func testVar() {
14+
var t = String()
15+
t = String()
16+
let _ = _borrow t
17+
}
18+
19+

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

0 commit comments

Comments
 (0)