Skip to content

Commit c97121d

Browse files
committed
[reference-binding] Add support for inout binding parsing/serialization.
1 parent 4b354f6 commit c97121d

17 files changed

+106
-42
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -388,9 +388,9 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
388388
IsStatic : 1
389389
);
390390

391-
SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 1+1+1+1+1+1,
391+
SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 2+1+1+1+1+1,
392392
/// Encodes whether this is a 'let' binding.
393-
Introducer : 1,
393+
Introducer : 2,
394394

395395
/// Whether this declaration captures the 'self' param under the same name.
396396
IsSelfParamCapture : 1,
@@ -5498,7 +5498,8 @@ class VarDecl : public AbstractStorageDecl {
54985498
public:
54995499
enum class Introducer : uint8_t {
55005500
Let = 0,
5501-
Var = 1
5501+
Var = 1,
5502+
InOut = 2,
55025503
};
55035504

55045505
protected:

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -964,8 +964,8 @@ ERROR(no_default_arg_closure,none,
964964
ERROR(no_default_arg_curried,none,
965965
"default arguments are not allowed in curried parameter lists", ())
966966
ERROR(var_pattern_in_var,none,
967-
"'%select{var|let}0' cannot appear nested inside another 'var' or "
968-
"'let' pattern", (unsigned))
967+
"'%select{let|inout|var}0' cannot appear nested inside another 'var', "
968+
"'let', or 'inout' pattern", (unsigned))
969969
ERROR(extra_var_in_multiple_pattern_list,none,
970970
"%0 must be bound in every pattern", (Identifier))
971971
ERROR(let_pattern_in_immutable_context,none,

include/swift/AST/Pattern.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class alignas(8) Pattern : public ASTAllocated<Pattern> {
7777

7878
SWIFT_INLINE_BITFIELD(BindingPattern, Pattern, 2,
7979
/// Corresponds to VarDecl::Introducer
80-
Introducer : 1
80+
Introducer : 2
8181
);
8282

8383
SWIFT_INLINE_BITFIELD(AnyPattern, Pattern, 1,
@@ -728,6 +728,17 @@ class BindingPattern : public Pattern {
728728

729729
bool isLet() const { return getIntroducer() == VarDecl::Introducer::Let; }
730730

731+
StringRef getIntroducerStringRef() const {
732+
switch (getIntroducer()) {
733+
case VarDecl::Introducer::Let:
734+
return "let";
735+
case VarDecl::Introducer::Var:
736+
return "var";
737+
case VarDecl::Introducer::InOut:
738+
return "inout";
739+
}
740+
}
741+
731742
SourceLoc getLoc() const { return VarLoc; }
732743
SourceRange getSourceRange() const {
733744
SourceLoc EndLoc = SubPattern->getSourceRange().End;

include/swift/Parse/PatternBindingState.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ struct PatternBindingState {
4444
/// nested let/var patterns are not permitted. This happens when parsing a
4545
/// 'let' decl or when parsing inside a 'let' pattern.
4646
InLet,
47+
48+
/// When InBindingPattern has this value, bound variables are mutable, and
49+
/// nested let/var/inout patterns are not permitted. This happens when
50+
/// parsing an 'inout' decl or when parsing inside an 'inout' pattern.
51+
InInOut,
4752
};
4853

4954
Kind kind;
@@ -58,6 +63,7 @@ struct PatternBindingState {
5863
auto kind = llvm::StringSwitch<Kind>(str)
5964
.Case("let", Kind::InLet)
6065
.Case("var", Kind::InVar)
66+
.Case("inout", Kind::InInOut)
6167
.Default(Kind::NotInBinding);
6268
return PatternBindingState(kind);
6369
}
@@ -71,6 +77,9 @@ struct PatternBindingState {
7177
case tok::kw_var:
7278
kind = InVar;
7379
break;
80+
case tok::kw_inout:
81+
kind = InInOut;
82+
break;
7483
default:
7584
break;
7685
}
@@ -86,6 +95,9 @@ struct PatternBindingState {
8695
case VarDecl::Introducer::Var:
8796
kind = InVar;
8897
break;
98+
case VarDecl::Introducer::InOut:
99+
kind = InInOut;
100+
break;
89101
}
90102
}
91103

@@ -101,6 +113,8 @@ struct PatternBindingState {
101113
return VarDecl::Introducer::Var;
102114
case Kind::InLet:
103115
return VarDecl::Introducer::Let;
116+
case Kind::InInOut:
117+
return VarDecl::Introducer::InOut;
104118
}
105119
}
106120

@@ -113,8 +127,10 @@ struct PatternBindingState {
113127
switch (kind) {
114128
case Kind::InLet:
115129
return 0;
116-
case Kind::InVar:
130+
case Kind::InInOut:
117131
return 1;
132+
case Kind::InVar:
133+
return 2;
118134
default:
119135
return None;
120136
}

lib/Parse/ParseDecl.cpp

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4678,6 +4678,7 @@ bool swift::isKeywordPossibleDeclStart(const Token &Tok) {
46784678
case tok::kw_func:
46794679
case tok::kw_import:
46804680
case tok::kw_init:
4681+
case tok::kw_inout:
46814682
case tok::kw_internal:
46824683
case tok::kw_let:
46834684
case tok::kw_operator:
@@ -5067,11 +5068,11 @@ Parser::parseDecl(ParseDeclOptions Flags,
50675068

50685069
bool HandlerAlreadyCalled = false;
50695070

5070-
auto parseLetOrVar = [&](bool HasLetOrVarKeyword) {
5071+
auto parseBindingIntroducer = [&](bool HasBindingIntroducerKeyword) {
50715072
// Collect all modifiers into a modifier list.
50725073
llvm::SmallVector<Decl *, 4> Entries;
50735074
DeclResult = parseDeclVar(Flags, Attributes, Entries, StaticLoc,
5074-
StaticSpelling, tryLoc, HasLetOrVarKeyword);
5075+
StaticSpelling, tryLoc, HasBindingIntroducerKeyword);
50755076
StaticLoc = SourceLoc(); // we handled static if present.
50765077
MayNeedOverrideCompletion = true;
50775078
if ((AttrStatus.hasCodeCompletion() || DeclResult.hasCodeCompletion())
@@ -5096,9 +5097,10 @@ Parser::parseDecl(ParseDeclOptions Flags,
50965097
case tok::kw_extension:
50975098
DeclResult = parseDeclExtension(Flags, Attributes);
50985099
break;
5100+
case tok::kw_inout:
50995101
case tok::kw_let:
51005102
case tok::kw_var: {
5101-
parseLetOrVar(/*HasLetOrVarKeyword=*/true);
5103+
parseBindingIntroducer(/*HasLetOrVarKeyword=*/true);
51025104
break;
51035105
}
51045106
case tok::kw_typealias:
@@ -5239,7 +5241,7 @@ Parser::parseDecl(ParseDeclOptions Flags,
52395241
diagnose(Tok.getLoc(), diag::expected_keyword_in_decl, "var",
52405242
DescriptiveKind)
52415243
.fixItInsert(Tok.getLoc(), "var ");
5242-
parseLetOrVar(/*HasLetOrVarKeyword=*/false);
5244+
parseBindingIntroducer(/*HasLetOrVarKeyword=*/false);
52435245
break;
52445246
}
52455247

@@ -7406,15 +7408,15 @@ void Parser::ParsedAccessors::classify(Parser &P, AbstractStorageDecl *storage,
74067408
}
74077409

74087410

7409-
/// Parse a 'var' or 'let' declaration, doing no token skipping on error.
7411+
/// Parse a 'var', 'let', or 'inout' declaration, doing no token skipping on error.
74107412
ParserResult<PatternBindingDecl>
74117413
Parser::parseDeclVar(ParseDeclOptions Flags,
74127414
DeclAttributes &Attributes,
74137415
SmallVectorImpl<Decl *> &Decls,
74147416
SourceLoc StaticLoc,
74157417
StaticSpellingKind StaticSpelling,
74167418
SourceLoc TryLoc,
7417-
bool HasLetOrVarKeyword) {
7419+
bool HasBindingKeyword) {
74187420
assert(StaticLoc.isInvalid() || StaticSpelling != StaticSpellingKind::None);
74197421

74207422
if (StaticLoc.isValid()) {
@@ -7432,11 +7434,13 @@ Parser::parseDeclVar(ParseDeclOptions Flags,
74327434
}
74337435
}
74347436

7435-
bool isLet = HasLetOrVarKeyword && Tok.is(tok::kw_let);
7436-
assert(!HasLetOrVarKeyword || Tok.getKind() == tok::kw_let ||
7437-
Tok.getKind() == tok::kw_var);
7437+
PatternBindingState newBindingContext = PatternBindingState::NotInBinding;
7438+
if (HasBindingKeyword)
7439+
newBindingContext = PatternBindingState(Tok);
7440+
assert(!HasBindingKeyword || Tok.getKind() == tok::kw_let ||
7441+
Tok.getKind() == tok::kw_var || Tok.getKind() == tok::kw_inout);
74387442

7439-
SourceLoc VarLoc = HasLetOrVarKeyword ? consumeToken() : Tok.getLoc();
7443+
SourceLoc VarLoc = newBindingContext.getIntroducer() ? consumeToken() : Tok.getLoc();
74407444

74417445
// If this is a var in the top-level of script/repl source file, wrap the
74427446
// PatternBindingDecl in a TopLevelCodeDecl, since it represents executable
@@ -7507,7 +7511,7 @@ Parser::parseDeclVar(ParseDeclOptions Flags,
75077511
// In our recursive parse, remember that we're in a var/let pattern.
75087512
llvm::SaveAndRestore<decltype(InBindingPattern)> T(
75097513
InBindingPattern,
7510-
isLet ? PatternBindingState::InLet : PatternBindingState::InVar);
7514+
newBindingContext.getPatternBindingStateForIntroducer(VarDecl::Introducer::Var));
75117515

75127516
// Track whether we are parsing an 'async let' pattern.
75137517
const auto hasAsyncAttr = Attributes.hasAttribute<AsyncAttr>();

lib/Parse/ParseExpr.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ ParserResult<Expr> Parser::parseExprSequence(Diag<> Message,
222222
if (Tok.getText() == "&&" &&
223223
peekToken().isAny(tok::pound_available, tok::pound_unavailable,
224224
tok::pound__hasSymbol, tok::kw_let, tok::kw_var,
225-
tok::kw_case))
225+
tok::kw_case, tok::kw_inout))
226226
goto done;
227227

228228
// Parse the operator.
@@ -1635,8 +1635,7 @@ ParserResult<Expr> Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) {
16351635
// will be resolved (or rejected) by sema when the overall refutable pattern
16361636
// it transformed from an expression into a pattern.
16371637
if ((InBindingPattern == PatternBindingState::ImplicitlyImmutable ||
1638-
InBindingPattern == PatternBindingState::InVar ||
1639-
InBindingPattern == PatternBindingState::InLet) &&
1638+
InBindingPattern.getIntroducer().hasValue()) &&
16401639
// If we have "case let x." or "case let x(", we parse x as a normal
16411640
// name, not a binding, because it is the start of an enum pattern or
16421641
// call pattern.

lib/Parse/ParsePattern.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
327327

328328
// If let or var is being used as an argument label, allow it but
329329
// generate a warning.
330-
if (!isClosure && Tok.isAny(tok::kw_let, tok::kw_var)) {
330+
if (!isClosure && Tok.isAny(tok::kw_let, tok::kw_var, tok::kw_inout)) {
331331
diagnose(Tok, diag::parameter_let_var_as_attr, Tok.getText())
332332
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
333333
}
@@ -1104,7 +1104,7 @@ ParserResult<Pattern> Parser::parsePattern() {
11041104
consumeToken(tok::code_complete);
11051105
}
11061106
return makeParserCodeCompletionStatus();
1107-
1107+
case tok::kw_inout:
11081108
case tok::kw_var:
11091109
case tok::kw_let: {
11101110
auto newBindingState = PatternBindingState(Tok);
@@ -1273,8 +1273,8 @@ ParserResult<Pattern> Parser::parseMatchingPattern(bool isExprBasic) {
12731273
// through the expr parser for ambiguous productions.
12741274

12751275
// Parse productions that can only be patterns.
1276-
if (Tok.isAny(tok::kw_var, tok::kw_let)) {
1277-
assert(Tok.isAny(tok::kw_let, tok::kw_var) && "expects var or let");
1276+
if (Tok.isAny(tok::kw_var, tok::kw_let, tok::kw_inout)) {
1277+
assert(Tok.isAny(tok::kw_let, tok::kw_var, tok::kw_inout) && "expects var or let");
12781278
auto newPatternBindingState = PatternBindingState(Tok);
12791279
SourceLoc varLoc = consumeToken();
12801280

@@ -1356,7 +1356,7 @@ Parser::parseMatchingPatternAsBinding(PatternBindingState newState,
13561356
}
13571357

13581358
bool Parser::isOnlyStartOfMatchingPattern() {
1359-
return Tok.isAny(tok::kw_var, tok::kw_let, tok::kw_is);
1359+
return Tok.isAny(tok::kw_var, tok::kw_let, tok::kw_is, tok::kw_inout);
13601360
}
13611361

13621362

@@ -1373,6 +1373,7 @@ static bool canParsePattern(Parser &P) {
13731373
case tok::kw__:
13741374
P.consumeToken();
13751375
return true;
1376+
case tok::kw_inout:
13761377
case tok::kw_let:
13771378
case tok::kw_var:
13781379
P.consumeToken();

lib/Parse/ParseStmt.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,7 +1568,7 @@ Parser::parseStmtConditionElement(SmallVectorImpl<StmtConditionElement> &result,
15681568

15691569
// Parse the basic expression case. If we have a leading let/var/case
15701570
// keyword or an assignment, then we know this is a binding.
1571-
if (Tok.isNot(tok::kw_let, tok::kw_var, tok::kw_case)) {
1571+
if (Tok.isNot(tok::kw_let, tok::kw_var, tok::kw_case, tok::kw_inout)) {
15721572
// If we lack it, then this is theoretically a boolean condition.
15731573
// However, we also need to handle migrating from Swift 2 syntax, in
15741574
// which a comma followed by an expression could actually be a pattern
@@ -1600,7 +1600,7 @@ Parser::parseStmtConditionElement(SmallVectorImpl<StmtConditionElement> &result,
16001600
}
16011601

16021602
SourceLoc IntroducerLoc;
1603-
if (Tok.isAny(tok::kw_let, tok::kw_var, tok::kw_case)) {
1603+
if (Tok.isAny(tok::kw_let, tok::kw_var, tok::kw_case, tok::kw_inout)) {
16041604
BindingKindStr = Tok.getText();
16051605
IntroducerLoc = consumeToken();
16061606
} else {

lib/Parse/Parser.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,7 @@ void Parser::skipListUntilDeclRBrace(SourceLoc startLoc, tok T1, tok T2) {
747747

748748
// Could have encountered something like `_ var:`
749749
// or `let foo:` or `var:`
750-
if (Tok.isAny(tok::kw_var, tok::kw_let)) {
750+
if (Tok.isAny(tok::kw_var, tok::kw_let, tok::kw_inout)) {
751751
if (possibleDeclStartsLine && !hasDelimiter) {
752752
break;
753753
}

lib/Sema/TypeCheckPattern.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ class ResolvePattern : public ASTVisitor<ResolvePattern,
290290
if (!HasVariable) {
291291
Context.Diags
292292
.diagnose(P->getLoc(), diag::var_pattern_didnt_bind_variables,
293-
P->isLet() ? "let" : "var")
293+
P->getIntroducerStringRef())
294294
.highlight(P->getSubPattern()->getSourceRange())
295295
.fixItRemove(P->getLoc());
296296
}

lib/Serialization/Deserialization.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2547,6 +2547,7 @@ getActualVarDeclIntroducer(serialization::VarDeclIntroducer raw) {
25472547
return swift::VarDecl::Introducer::ID;
25482548
CASE(Let)
25492549
CASE(Var)
2550+
CASE(InOut)
25502551
}
25512552
#undef CASE
25522553
return None;

lib/Serialization/ModuleFormat.h

Lines changed: 4 additions & 3 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 = 747; // `consuming` and `borrowing` decl modifiers
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 748; // inout binding
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///
@@ -346,9 +346,10 @@ using ParamDeclSpecifierField = BCFixed<3>;
346346
// the module version.
347347
enum class VarDeclIntroducer : uint8_t {
348348
Let = 0,
349-
Var = 1
349+
Var = 1,
350+
InOut = 2,
350351
};
351-
using VarDeclIntroducerField = BCFixed<1>;
352+
using VarDeclIntroducerField = BCFixed<2>;
352353

353354
// These IDs must \em not be renumbered or reordered without incrementing
354355
// the module version.

lib/Serialization/Serialization.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2448,6 +2448,8 @@ static uint8_t getRawStableVarDeclIntroducer(swift::VarDecl::Introducer intr) {
24482448
return uint8_t(serialization::VarDeclIntroducer::Let);
24492449
case swift::VarDecl::Introducer::Var:
24502450
return uint8_t(serialization::VarDeclIntroducer::Var);
2451+
case swift::VarDecl::Introducer::InOut:
2452+
return uint8_t(serialization::VarDeclIntroducer::InOut);
24512453
}
24522454
llvm_unreachable("bad variable decl introducer kind");
24532455
}

test/Constraints/patterns.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,8 @@ for (var x) in 0...100 {} // expected-warning{{variable 'x' was never used; cons
194194
for var x in 0...100 {} // rdar://20167543 expected-warning{{variable 'x' was never used; consider replacing with '_' or removing it}}
195195
for (let x) in 0...100 { _ = x} // expected-error {{'let' pattern cannot appear nested in an already immutable context}}
196196

197-
var (let y) = 42 // expected-error {{'let' cannot appear nested inside another 'var' or 'let' pattern}}
198-
let (var z) = 42 // expected-error {{'var' cannot appear nested inside another 'var' or 'let' pattern}}
197+
var (let y) = 42 // expected-error {{'let' cannot appear nested inside another 'var', 'let', or 'inout' pattern}}
198+
let (var z) = 42 // expected-error {{'var' cannot appear nested inside another 'var', 'let', or 'inout' pattern}}
199199

200200

201201
// Crash when re-typechecking EnumElementPattern.

0 commit comments

Comments
 (0)