Skip to content

Commit 6706fea

Browse files
committed
Parse _borrowing x in patterns as a borrow binding.
Treat it as a contextual keyword when followed by an identifier, like our other ownership-related declarations and operators.
1 parent 643fa6c commit 6706fea

File tree

14 files changed

+190
-40
lines changed

14 files changed

+190
-40
lines changed

include/swift/AST/Decl.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5909,7 +5909,21 @@ class VarDecl : public AbstractStorageDecl {
59095909
Let = 0,
59105910
Var = 1,
59115911
InOut = 2,
5912+
Borrowing = 3,
59125913
};
5914+
5915+
static StringRef getIntroducerStringRef(Introducer i) {
5916+
switch (i) {
5917+
case VarDecl::Introducer::Let:
5918+
return "let";
5919+
case VarDecl::Introducer::Var:
5920+
return "var";
5921+
case VarDecl::Introducer::InOut:
5922+
return "inout";
5923+
case VarDecl::Introducer::Borrowing:
5924+
return "_borrowing";
5925+
}
5926+
}
59135927

59145928
protected:
59155929
PointerUnion<PatternBindingDecl *,
@@ -6156,6 +6170,10 @@ class VarDecl : public AbstractStorageDecl {
61566170
Introducer getIntroducer() const {
61576171
return Introducer(Bits.VarDecl.Introducer);
61586172
}
6173+
6174+
StringRef getIntroducerStringRef() const {
6175+
return getIntroducerStringRef(getIntroducer());
6176+
}
61596177

61606178
CaptureListExpr *getParentCaptureList() const {
61616179
if (!Parent)

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,8 @@ ERROR(extra_var_in_multiple_pattern_list,none,
996996
"%0 must be bound in every pattern", (Identifier))
997997
ERROR(let_pattern_in_immutable_context,none,
998998
"'let' pattern cannot appear nested in an already immutable context", ())
999+
ERROR(borrowing_subpattern_unsupported,none,
1000+
"'_borrowing' pattern modifier must be directly applied to pattern variable name", ())
9991001
ERROR(specifier_must_have_type,none,
10001002
"%0 arguments must have a type specified", (StringRef))
10011003
ERROR(expected_rparen_parameter,PointsToFirstBadToken,

include/swift/AST/Pattern.h

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -817,17 +817,8 @@ class BindingPattern : public Pattern {
817817
return VP;
818818
}
819819

820-
bool isLet() const { return getIntroducer() == VarDecl::Introducer::Let; }
821-
822820
StringRef getIntroducerStringRef() const {
823-
switch (getIntroducer()) {
824-
case VarDecl::Introducer::Let:
825-
return "let";
826-
case VarDecl::Introducer::Var:
827-
return "var";
828-
case VarDecl::Introducer::InOut:
829-
return "inout";
830-
}
821+
return VarDecl::getIntroducerStringRef(getIntroducer());
831822
}
832823

833824
SourceLoc getLoc() const { return VarLoc; }

include/swift/Parse/PatternBindingState.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ struct PatternBindingState {
9090
: kind(NotInBinding) {
9191
switch (introducer) {
9292
case VarDecl::Introducer::Let:
93+
case VarDecl::Introducer::Borrowing:
9394
kind = InLet;
9495
break;
9596
case VarDecl::Introducer::Var:

lib/AST/ASTDumper.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,9 @@ static StringRef getDumpString(RequirementKind kind) {
440440

441441
llvm_unreachable("Unhandled RequirementKind in switch.");
442442
}
443+
static StringRef getDumpString(StringRef s) {
444+
return s;
445+
}
443446
static unsigned getDumpString(unsigned value) {
444447
return value;
445448
}
@@ -1015,7 +1018,8 @@ namespace {
10151018
printFoot();
10161019
}
10171020
void visitBindingPattern(BindingPattern *P, StringRef label) {
1018-
printCommon(P, P->isLet() ? "pattern_let" : "pattern_var", label);
1021+
printCommon(P, "pattern_binding", label);
1022+
printField(P->getIntroducerStringRef(), "kind");
10191023
printRec(P->getSubPattern());
10201024
printFoot();
10211025
}

lib/AST/ASTPrinter.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,7 +1415,7 @@ void PrintAST::printPattern(const Pattern *pattern) {
14151415
case PatternKind::Binding: {
14161416
auto bPattern = cast<BindingPattern>(pattern);
14171417
Printer.printIntroducerKeyword(
1418-
bPattern->isLet() ? "let" : "var",
1418+
bPattern->getIntroducerStringRef(),
14191419
Options, " ");
14201420
printPattern(bPattern->getSubPattern());
14211421
}
@@ -4634,7 +4634,10 @@ void PrintAST::visitVarDecl(VarDecl *decl) {
46344634
if (decl->getKind() == DeclKind::Var || Options.PrintParameterSpecifiers) {
46354635
// Map all non-let specifiers to 'var'. This is not correct, but
46364636
// SourceKit relies on this for info about parameter decls.
4637-
Printer.printIntroducerKeyword(decl->isLet() ? "let" : "var", Options, " ");
4637+
4638+
Printer.printIntroducerKeyword(
4639+
decl->getIntroducer() == VarDecl::Introducer::Let ? "let" : "var",
4640+
Options, " ");
46384641
}
46394642
printContextIfNeeded(decl);
46404643
recordDeclLoc(decl,

lib/AST/Decl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7758,7 +7758,8 @@ bool VarDecl::isLet() const {
77587758
if (auto *PD = dyn_cast<ParamDecl>(this)) {
77597759
return PD->isImmutableInFunctionBody();
77607760
}
7761-
return getIntroducer() == Introducer::Let;
7761+
return getIntroducer() == Introducer::Let
7762+
|| getIntroducer() == Introducer::Borrowing;
77627763
}
77637764

77647765
bool VarDecl::isAsyncLet() const {

lib/AST/Pattern.cpp

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,15 @@ DescriptivePatternKind Pattern::getDescriptiveKind() const {
5151
TRIVIAL_PATTERN_KIND(Expr);
5252

5353
case PatternKind::Binding:
54-
return cast<BindingPattern>(this)->isLet() ? DescriptivePatternKind::Let
55-
: DescriptivePatternKind::Var;
54+
switch (cast<BindingPattern>(this)->getIntroducer()) {
55+
case VarDecl::Introducer::Let:
56+
case VarDecl::Introducer::Borrowing:
57+
return DescriptivePatternKind::Let;
58+
59+
case VarDecl::Introducer::Var:
60+
case VarDecl::Introducer::InOut:
61+
return DescriptivePatternKind::Var;
62+
}
5663
}
5764
#undef TRIVIAL_PATTERN_KIND
5865
llvm_unreachable("bad DescriptivePatternKind");
@@ -770,7 +777,24 @@ Pattern::getOwnership(
770777
void visitNamedPattern(NamedPattern *p) {
771778
// `var` and `let` bindings consume the matched value.
772779
// TODO: borrowing/mutating/consuming parameters
773-
increaseOwnership(ValueOwnership::Owned, p);
780+
switch (p->getDecl()->getIntroducer()) {
781+
case VarDecl::Introducer::Let:
782+
case VarDecl::Introducer::Var:
783+
// `let` and `var` consume the bound value to move it into a new
784+
// independent variable.
785+
increaseOwnership(ValueOwnership::Owned, p);
786+
break;
787+
788+
case VarDecl::Introducer::InOut:
789+
// `inout` bindings modify the value in-place.
790+
increaseOwnership(ValueOwnership::InOut, p);
791+
break;
792+
793+
case VarDecl::Introducer::Borrowing:
794+
// `borrow` bindings borrow parts of the value in-place so they don't
795+
// need stronger access to the subject value.
796+
break;
797+
}
774798
}
775799

776800
void visitAnyPattern(AnyPattern *p) {

lib/Parse/ParsePattern.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,6 +1317,42 @@ ParserResult<Pattern> Parser::parseMatchingPattern(bool isExprBasic) {
13171317
return parseMatchingPatternAsBinding(newPatternBindingState, varLoc,
13181318
isExprBasic);
13191319
}
1320+
1321+
// The `borrowing` modifier is a contextual keyword, so it's only accepted
1322+
// directly applied to a binding name, as in `case .foo(borrowing x)`.
1323+
if (Context.LangOpts.hasFeature(Feature::BorrowingSwitch)) {
1324+
if (Tok.isContextualKeyword("_borrowing")
1325+
&& peekToken().isAny(tok::identifier, tok::kw_self, tok::dollarident,
1326+
tok::code_complete)
1327+
&& !peekToken().isAtStartOfLine()) {
1328+
Tok.setKind(tok::contextual_keyword);
1329+
SourceLoc borrowingLoc = consumeToken();
1330+
1331+
// If we have `case borrowing x.`, `x(`, `x[`, or `x<` then this looks
1332+
// like an attempt to include a subexpression under a `borrowing`
1333+
// binding, which isn't yet supported.
1334+
if (peekToken().isAny(tok::period, tok::period_prefix, tok::l_paren,
1335+
tok::l_square)
1336+
|| (peekToken().isAnyOperator() && peekToken().getText().equals("<"))) {
1337+
1338+
// Diagnose the unsupported production.
1339+
diagnose(Tok.getLoc(),
1340+
diag::borrowing_subpattern_unsupported);
1341+
1342+
// Recover by parsing as if it was supported.
1343+
return parseMatchingPattern(isExprBasic);
1344+
}
1345+
Identifier name;
1346+
SourceLoc nameLoc = consumeIdentifier(name,
1347+
/*diagnoseDollarPrefix*/ false);
1348+
auto namedPattern = createBindingFromPattern(nameLoc, name,
1349+
VarDecl::Introducer::Borrowing);
1350+
auto bindPattern = new (Context) BindingPattern(
1351+
borrowingLoc, VarDecl::Introducer::Borrowing, namedPattern);
1352+
1353+
return makeParserResult(bindPattern);
1354+
}
1355+
}
13201356

13211357
// matching-pattern ::= 'is' type
13221358
if (Tok.is(tok::kw_is)) {
@@ -1396,6 +1432,15 @@ Parser::parseMatchingPatternAsBinding(PatternBindingState newState,
13961432
}
13971433

13981434
bool Parser::isOnlyStartOfMatchingPattern() {
1435+
if (Context.LangOpts.hasFeature(Feature::BorrowingSwitch)) {
1436+
if (Tok.isContextualKeyword("_borrowing")
1437+
&& peekToken().isAny(tok::identifier, tok::kw_self, tok::dollarident,
1438+
tok::code_complete)
1439+
&& !peekToken().isAtStartOfLine()) {
1440+
return true;
1441+
}
1442+
}
1443+
13991444
return Tok.isAny(tok::kw_var, tok::kw_let, tok::kw_is) ||
14001445
(Context.LangOpts.hasFeature(Feature::ReferenceBindings) &&
14011446
Tok.isAny(tok::kw_inout));

lib/Sema/MiscDiagnostics.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3545,8 +3545,9 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
35453545
foundVP = VP;
35463546
});
35473547

3548-
if (foundVP && !foundVP->isLet())
3548+
if (foundVP && foundVP->getIntroducer() != VarDecl::Introducer::Let) {
35493549
FixItLoc = foundVP->getLoc();
3550+
}
35503551
}
35513552

35523553
// If this is a parameter explicitly marked 'var', remove it.

0 commit comments

Comments
 (0)