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.

lib/Serialization/Deserialization.cpp

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,21 @@ ParameterList *ModuleFile::readParameterList() {
514514
return ParameterList::create(getContext(), params);
515515
}
516516

517+
static llvm::Optional<swift::VarDecl::Introducer>
518+
getActualVarDeclIntroducer(serialization::VarDeclIntroducer raw) {
519+
switch (raw) {
520+
#define CASE(ID) \
521+
case serialization::VarDeclIntroducer::ID: \
522+
return swift::VarDecl::Introducer::ID;
523+
CASE(Let)
524+
CASE(Var)
525+
CASE(InOut)
526+
CASE(Borrowing)
527+
}
528+
#undef CASE
529+
return llvm::None;
530+
}
531+
517532
Expected<Pattern *> ModuleFile::readPattern(DeclContext *owningDC) {
518533
// Currently, the only case in which this function can fail (return an error)
519534
// is when reading a pattern for a single variable declaration.
@@ -643,15 +658,18 @@ Expected<Pattern *> ModuleFile::readPattern(DeclContext *owningDC) {
643658
return result;
644659
}
645660
case decls_block::VAR_PATTERN: {
646-
bool isLet;
647-
BindingPatternLayout::readRecord(scratch, isLet);
661+
unsigned rawIntroducer;
662+
BindingPatternLayout::readRecord(scratch, rawIntroducer);
648663

649664
Pattern *subPattern = readPatternUnchecked(owningDC);
650665

666+
auto introducer = getActualVarDeclIntroducer(
667+
(serialization::VarDeclIntroducer) rawIntroducer);
668+
if (!introducer)
669+
return diagnoseFatal();
670+
651671
auto result = BindingPattern::createImplicit(
652-
getContext(),
653-
isLet ? VarDecl::Introducer::Let : VarDecl::Introducer::Var,
654-
subPattern);
672+
getContext(), *introducer, subPattern);
655673
if (Type interfaceType = subPattern->getDelayedInterfaceType())
656674
result->setDelayedInterfaceType(interfaceType, owningDC);
657675
else
@@ -2871,20 +2889,6 @@ getActualParamDeclSpecifier(serialization::ParamDeclSpecifier raw) {
28712889
return llvm::None;
28722890
}
28732891

2874-
static llvm::Optional<swift::VarDecl::Introducer>
2875-
getActualVarDeclIntroducer(serialization::VarDeclIntroducer raw) {
2876-
switch (raw) {
2877-
#define CASE(ID) \
2878-
case serialization::VarDeclIntroducer::ID: \
2879-
return swift::VarDecl::Introducer::ID;
2880-
CASE(Let)
2881-
CASE(Var)
2882-
CASE(InOut)
2883-
}
2884-
#undef CASE
2885-
return llvm::None;
2886-
}
2887-
28882892
static llvm::Optional<swift::OpaqueReadOwnership>
28892893
getActualOpaqueReadOwnership(unsigned rawKind) {
28902894
switch (serialization::OpaqueReadOwnership(rawKind)) {

lib/Serialization/ModuleFormat.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 844; // remove IsolatedAttr
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 845; // borrowing var introducer
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///
@@ -369,6 +369,7 @@ enum class VarDeclIntroducer : uint8_t {
369369
Let = 0,
370370
Var = 1,
371371
InOut = 2,
372+
Borrowing = 3,
372373
};
373374
using VarDeclIntroducerField = BCFixed<2>;
374375

@@ -1897,7 +1898,7 @@ namespace decls_block {
18971898

18981899
using BindingPatternLayout = BCRecordLayout<
18991900
VAR_PATTERN,
1900-
BCFixed<1> // isLet?
1901+
BCFixed<2> // introducer (var, let, etc.)
19011902
// The sub-pattern trails the record.
19021903
>;
19031904

lib/Serialization/Serialization.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2560,6 +2560,8 @@ static uint8_t getRawStableVarDeclIntroducer(swift::VarDecl::Introducer intr) {
25602560
return uint8_t(serialization::VarDeclIntroducer::Var);
25612561
case swift::VarDecl::Introducer::InOut:
25622562
return uint8_t(serialization::VarDeclIntroducer::InOut);
2563+
case swift::VarDecl::Introducer::Borrowing:
2564+
return uint8_t(serialization::VarDeclIntroducer::Borrowing);
25632565
}
25642566
llvm_unreachable("bad variable decl introducer kind");
25652567
}
@@ -3674,7 +3676,7 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
36743676

36753677
unsigned abbrCode = S.DeclTypeAbbrCodes[BindingPatternLayout::Code];
36763678
BindingPatternLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode,
3677-
var->isLet());
3679+
getRawStableVarDeclIntroducer(var->getIntroducer()));
36783680
writePattern(var->getSubPattern());
36793681
break;
36803682
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: %target-swift-frontend -enable-experimental-feature BorrowingSwitch -typecheck -verify %s
2+
3+
struct Payload: ~Copyable {
4+
var x: Int
5+
var y: String
6+
}
7+
8+
enum Foo: ~Copyable {
9+
case payload(Payload)
10+
case noPayload
11+
}
12+
13+
enum Bar: ~Copyable {
14+
case payload(Foo)
15+
case noPayload
16+
17+
var member: Bar { fatalError() }
18+
}
19+
20+
struct SourceBreakTest {
21+
func foo() -> Bar {}
22+
23+
func callAsFunction() -> Bar { fatalError() }
24+
}
25+
26+
let _borrowing = SourceBreakTest()
27+
28+
func ~=(_: borrowing Bar, _: borrowing Bar) -> Bool { fatalError() }
29+
30+
func useBorrowBar(_: borrowing Bar) { fatalError() }
31+
func useBorrowFoo(_: borrowing Foo) { fatalError() }
32+
func useBorrowPayload(_: borrowing Payload) { fatalError() }
33+
34+
func testBorrowingPatterns(bar: borrowing Bar) {
35+
switch bar {
36+
case _borrowing .foo(): // parses as `_borrowing.foo()` as before
37+
break
38+
case _borrowing (): // parses as `_borrowing()` as before
39+
break
40+
41+
case _borrowing x:
42+
useBorrowBar(x)
43+
44+
case .payload(_borrowing x):
45+
useBorrowFoo(x)
46+
47+
case _borrowing x.member: // expected-error{{'_borrowing' pattern modifier must be directly applied to pattern variable name}} expected-error{{cannot find 'x' in scope}}
48+
break
49+
50+
default:
51+
break
52+
}
53+
}

0 commit comments

Comments
 (0)